Merge pull request #206 from authorizerdev/feat/2fa
feat: add mutifactor authentication
This commit is contained in:
commit
0714b4360b
14
Makefile
14
Makefile
|
@ -11,14 +11,26 @@ clean:
|
|||
rm -rf build
|
||||
test:
|
||||
rm -rf server/test/test.db && rm -rf test.db && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test
|
||||
test-mongodb:
|
||||
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
||||
cd server && go clean --testcache && TEST_DBS="mongodb" go test -p 1 -v ./test
|
||||
docker rm -vf authorizer_mongodb_db
|
||||
test-scylladb:
|
||||
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
|
||||
cd server && go clean --testcache && TEST_DBS="scylladb" go test -p 1 -v ./test
|
||||
docker rm -vf authorizer_scylla_db
|
||||
test-arangodb:
|
||||
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
||||
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
|
||||
docker rm -vf authorizer_arangodb
|
||||
test-all-db:
|
||||
rm -rf server/test/test.db && rm -rf test.db
|
||||
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
|
||||
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
||||
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
||||
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb" go test -p 1 -v ./test
|
||||
docker rm -vf authorizer_mongodb_db
|
||||
docker rm -vf authorizer_scylla_db
|
||||
docker rm -vf authorizer_mongodb_db
|
||||
docker rm -vf authorizer_arangodb
|
||||
generate:
|
||||
cd server && go get github.com/99designs/gqlgen/cmd@v0.14.0 && go run github.com/99designs/gqlgen generate
|
||||
|
|
30
app/package-lock.json
generated
30
app/package-lock.json
generated
|
@ -9,7 +9,7 @@
|
|||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^0.25.0",
|
||||
"@authorizerdev/authorizer-react": "^0.26.0-beta.0",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
|
@ -26,9 +26,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@authorizerdev/authorizer-js": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.14.0.tgz",
|
||||
"integrity": "sha512-cpeeFrmG623QPLn+nf+ACHayZYqW8xokIidGikeboBDJtuAAQB50a54/7HwLHriG2FB7WvPuHQ/9LFFX//N1lg==",
|
||||
"version": "0.17.0-beta.1",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.17.0-beta.1.tgz",
|
||||
"integrity": "sha512-jUlFUrs4Ys6LZ5hclPeRt84teygi+bA57d/IpV9GAqOrfifv70jkFeDln4+Bs0mZk74el23Xn+DR9380mqE4Cg==",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
|
@ -37,11 +37,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@authorizerdev/authorizer-react": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.25.0.tgz",
|
||||
"integrity": "sha512-Dt2rZf+cGCVb8dqcJ/9l8Trx+QeXnTdfhER6r/cq0iOnFC9MqWzQPB3RgrlUoMLHtZvKNDXIk1HvfD5hSX9lhw==",
|
||||
"version": "0.26.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.26.0-beta.0.tgz",
|
||||
"integrity": "sha512-YfyiGYBmbsp3tLWIxOrOZ/hUTCmdMXVE9SLE8m1xsFsxzJJlUhepp0AMahSbH5EyLj5bchOhOw/rzgpnDZDvMw==",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-js": "^0.14.0",
|
||||
"@authorizerdev/authorizer-js": "^0.17.0-beta.1",
|
||||
"final-form": "^4.20.2",
|
||||
"react-final-form": "^6.5.3",
|
||||
"styled-components": "^5.3.0"
|
||||
|
@ -852,19 +852,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-js": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.14.0.tgz",
|
||||
"integrity": "sha512-cpeeFrmG623QPLn+nf+ACHayZYqW8xokIidGikeboBDJtuAAQB50a54/7HwLHriG2FB7WvPuHQ/9LFFX//N1lg==",
|
||||
"version": "0.17.0-beta.1",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.17.0-beta.1.tgz",
|
||||
"integrity": "sha512-jUlFUrs4Ys6LZ5hclPeRt84teygi+bA57d/IpV9GAqOrfifv70jkFeDln4+Bs0mZk74el23Xn+DR9380mqE4Cg==",
|
||||
"requires": {
|
||||
"node-fetch": "^2.6.1"
|
||||
}
|
||||
},
|
||||
"@authorizerdev/authorizer-react": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.25.0.tgz",
|
||||
"integrity": "sha512-Dt2rZf+cGCVb8dqcJ/9l8Trx+QeXnTdfhER6r/cq0iOnFC9MqWzQPB3RgrlUoMLHtZvKNDXIk1HvfD5hSX9lhw==",
|
||||
"version": "0.26.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.26.0-beta.0.tgz",
|
||||
"integrity": "sha512-YfyiGYBmbsp3tLWIxOrOZ/hUTCmdMXVE9SLE8m1xsFsxzJJlUhepp0AMahSbH5EyLj5bchOhOw/rzgpnDZDvMw==",
|
||||
"requires": {
|
||||
"@authorizerdev/authorizer-js": "^0.14.0",
|
||||
"@authorizerdev/authorizer-js": "^0.17.0-beta.1",
|
||||
"final-form": "^4.20.2",
|
||||
"react-final-form": "^6.5.3",
|
||||
"styled-components": "^5.3.0"
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"author": "Lakhan Samani",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^0.25.0",
|
||||
"@authorizerdev/authorizer-react": "^0.26.0-beta.0",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
|
|
|
@ -108,7 +108,7 @@ const OAuthConfig = ({
|
|||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
|
||||
placeholder="Google Secret"
|
||||
placeholder="Google Client Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
|
@ -146,7 +146,7 @@ const OAuthConfig = ({
|
|||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
|
||||
placeholder="Github Secret"
|
||||
placeholder="Github Client Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
|
@ -184,7 +184,7 @@ const OAuthConfig = ({
|
|||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
|
||||
placeholder="Facebook Secret"
|
||||
placeholder="Facebook Client Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
|
@ -260,7 +260,7 @@ const OAuthConfig = ({
|
|||
fieldVisibility={fieldVisibility}
|
||||
setFieldVisibility={setFieldVisibility}
|
||||
inputType={HiddenInputType.APPLE_CLIENT_SECRET}
|
||||
placeholder="Apple CLient Secret"
|
||||
placeholder="Apple Client Secret"
|
||||
/>
|
||||
</Center>
|
||||
</Flex>
|
||||
|
|
|
@ -89,6 +89,7 @@ export const UserDetailsQuery = `
|
|||
roles
|
||||
created_at
|
||||
revoked_timestamp
|
||||
is_multi_factor_auth_enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ interface userDataTypes {
|
|||
roles: [string];
|
||||
created_at: number;
|
||||
revoked_timestamp: number;
|
||||
is_multi_factor_auth_enabled?: boolean;
|
||||
}
|
||||
|
||||
const enum updateAccessActions {
|
||||
|
@ -250,6 +251,34 @@ export default function Users() {
|
|||
break;
|
||||
}
|
||||
};
|
||||
const multiFactorAuthUpdateHandler = async (user: userDataTypes) => {
|
||||
const res = await client
|
||||
.mutation(UpdateUser, {
|
||||
params: {
|
||||
id: user.id,
|
||||
is_multi_factor_auth_enabled: !user.is_multi_factor_auth_enabled,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
if (res.data?._update_user?.id) {
|
||||
toast({
|
||||
title: `Multi factor authentication ${
|
||||
user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
|
||||
} for user`,
|
||||
isClosable: true,
|
||||
status: 'success',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
updateUserList();
|
||||
return;
|
||||
}
|
||||
toast({
|
||||
title: 'Multi factor authentication update failed for user',
|
||||
isClosable: true,
|
||||
status: 'error',
|
||||
position: 'bottom-right',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box m="5" py="5" px="10" bg="white" rounded="md">
|
||||
|
@ -273,6 +302,7 @@ export default function Users() {
|
|||
<Th>Roles</Th>
|
||||
<Th>Verified</Th>
|
||||
<Th>Access</Th>
|
||||
<Th>MFA</Th>
|
||||
<Th>Actions</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
|
@ -305,6 +335,19 @@ export default function Users() {
|
|||
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
|
||||
</Tag>
|
||||
</Td>
|
||||
<Td>
|
||||
<Tag
|
||||
size="sm"
|
||||
variant="outline"
|
||||
colorScheme={
|
||||
user.is_multi_factor_auth_enabled ? 'green' : 'red'
|
||||
}
|
||||
>
|
||||
{user.is_multi_factor_auth_enabled
|
||||
? 'Enabled'
|
||||
: 'Disabled'}
|
||||
</Tag>
|
||||
</Td>
|
||||
<Td>
|
||||
<Menu>
|
||||
<MenuButton as={Button} variant="unstyled" size="sm">
|
||||
|
@ -357,6 +400,19 @@ export default function Users() {
|
|||
Revoke Access
|
||||
</MenuItem>
|
||||
)}
|
||||
{user.is_multi_factor_auth_enabled ? (
|
||||
<MenuItem
|
||||
onClick={() => multiFactorAuthUpdateHandler(user)}
|
||||
>
|
||||
Disable MFA
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem
|
||||
onClick={() => multiFactorAuthUpdateHandler(user)}
|
||||
>
|
||||
Enable MFA
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</Td>
|
||||
|
|
|
@ -47,6 +47,8 @@ const (
|
|||
EnvKeySmtpPassword = "SMTP_PASSWORD"
|
||||
// EnvKeySenderEmail key for env variable SENDER_EMAIL
|
||||
EnvKeySenderEmail = "SENDER_EMAIL"
|
||||
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
|
||||
EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED"
|
||||
// EnvKeyJwtType key for env variable JWT_TYPE
|
||||
EnvKeyJwtType = "JWT_TYPE"
|
||||
// EnvKeyJwtSecret key for env variable JWT_SECRET
|
||||
|
@ -117,6 +119,12 @@ const (
|
|||
EnvKeyDisableRedisForEnv = "DISABLE_REDIS_FOR_ENV"
|
||||
// EnvKeyDisableStrongPassword key for env variable DISABLE_STRONG_PASSWORD
|
||||
EnvKeyDisableStrongPassword = "DISABLE_STRONG_PASSWORD"
|
||||
// EnvKeyEnforceMultiFactorAuthentication is key for env variable ENFORCE_MULTI_FACTOR_AUTHENTICATION
|
||||
// If enforced and changed later on, existing user will have MFA but new user will not have MFA
|
||||
EnvKeyEnforceMultiFactorAuthentication = "ENFORCE_MULTI_FACTOR_AUTHENTICATION"
|
||||
// EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION
|
||||
// this variable is used to completely disable multi factor authentication. It will have no effect on profile preference
|
||||
EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION"
|
||||
|
||||
// Slice variables
|
||||
// EnvKeyRoles key for env variable ROLES
|
||||
|
|
|
@ -9,6 +9,7 @@ type CollectionList struct {
|
|||
Webhook string
|
||||
WebhookLog string
|
||||
EmailTemplate string
|
||||
OTP string
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -23,5 +24,6 @@ var (
|
|||
Webhook: Prefix + "webhooks",
|
||||
WebhookLog: Prefix + "webhook_logs",
|
||||
EmailTemplate: Prefix + "email_templates",
|
||||
OTP: Prefix + "otps",
|
||||
}
|
||||
)
|
||||
|
|
12
server/db/models/otp.go
Normal file
12
server/db/models/otp.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package models
|
||||
|
||||
// OTP model for database
|
||||
type OTP struct {
|
||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||
Otp string `json:"otp" bson:"otp" cql:"otp"`
|
||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
}
|
|
@ -14,51 +14,53 @@ type User struct {
|
|||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
|
||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
|
||||
|
||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
|
||||
Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
|
||||
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
|
||||
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
|
||||
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
|
||||
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
|
||||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
|
||||
Gender *string `json:"gender" bson:"gender" cql:"gender"`
|
||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
|
||||
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
|
||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
|
||||
Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
|
||||
Roles string `json:"roles" bson:"roles" cql:"roles"`
|
||||
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
|
||||
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
|
||||
Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
|
||||
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
|
||||
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
|
||||
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
|
||||
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
|
||||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
|
||||
Gender *string `json:"gender" bson:"gender" cql:"gender"`
|
||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
|
||||
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
|
||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
|
||||
Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
|
||||
Roles string `json:"roles" bson:"roles" cql:"roles"`
|
||||
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
|
||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||
}
|
||||
|
||||
func (user *User) AsAPIUser() *model.User {
|
||||
isEmailVerified := user.EmailVerifiedAt != nil
|
||||
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
|
||||
|
||||
id := user.ID
|
||||
if strings.Contains(id, Collections.WebhookLog+"/") {
|
||||
id = strings.TrimPrefix(id, Collections.WebhookLog+"/")
|
||||
}
|
||||
// id := user.ID
|
||||
// if strings.Contains(id, Collections.User+"/") {
|
||||
// id = strings.TrimPrefix(id, Collections.User+"/")
|
||||
// }
|
||||
return &model.User{
|
||||
ID: id,
|
||||
Email: user.Email,
|
||||
EmailVerified: isEmailVerified,
|
||||
SignupMethods: user.SignupMethods,
|
||||
GivenName: user.GivenName,
|
||||
FamilyName: user.FamilyName,
|
||||
MiddleName: user.MiddleName,
|
||||
Nickname: user.Nickname,
|
||||
PreferredUsername: refs.NewStringRef(user.Email),
|
||||
Gender: user.Gender,
|
||||
Birthdate: user.Birthdate,
|
||||
PhoneNumber: user.PhoneNumber,
|
||||
PhoneNumberVerified: &isPhoneVerified,
|
||||
Picture: user.Picture,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
RevokedTimestamp: user.RevokedTimestamp,
|
||||
CreatedAt: refs.NewInt64Ref(user.CreatedAt),
|
||||
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
EmailVerified: isEmailVerified,
|
||||
SignupMethods: user.SignupMethods,
|
||||
GivenName: user.GivenName,
|
||||
FamilyName: user.FamilyName,
|
||||
MiddleName: user.MiddleName,
|
||||
Nickname: user.Nickname,
|
||||
PreferredUsername: refs.NewStringRef(user.Email),
|
||||
Gender: user.Gender,
|
||||
Birthdate: user.Birthdate,
|
||||
PhoneNumber: user.PhoneNumber,
|
||||
PhoneNumberVerified: &isPhoneVerified,
|
||||
Picture: user.Picture,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
RevokedTimestamp: user.RevokedTimestamp,
|
||||
IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled,
|
||||
CreatedAt: refs.NewInt64Ref(user.CreatedAt),
|
||||
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ type VerificationRequest struct {
|
|||
|
||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||
id := v.ID
|
||||
if strings.Contains(id, Collections.WebhookLog+"/") {
|
||||
id = strings.TrimPrefix(id, Collections.WebhookLog+"/")
|
||||
if strings.Contains(id, Collections.VerificationRequest+"/") {
|
||||
id = strings.TrimPrefix(id, Collections.VerificationRequest+"/")
|
||||
}
|
||||
|
||||
return &model.VerificationRequest{
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.EmailTemplate) (*model.EmailTemplate, error) {
|
||||
if emailTemplate.ID == "" {
|
||||
emailTemplate.ID = uuid.New().String()
|
||||
emailTemplate.Key = emailTemplate.ID
|
||||
}
|
||||
|
||||
emailTemplate.Key = emailTemplate.ID
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
func (p *provider) AddEnv(ctx context.Context, env models.Env) (models.Env, error) {
|
||||
if env.ID == "" {
|
||||
env.ID = uuid.New().String()
|
||||
env.Key = env.ID
|
||||
}
|
||||
|
||||
env.CreatedAt = time.Now().Unix()
|
||||
|
|
92
server/db/providers/arangodb/otp.go
Normal file
92
server/db/providers/arangodb/otp.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
package arangodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// UpsertOTP to add or update otp
|
||||
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
|
||||
otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
|
||||
shouldCreate := false
|
||||
if otp == nil {
|
||||
id := uuid.NewString()
|
||||
otp = &models.OTP{
|
||||
ID: id,
|
||||
Key: id,
|
||||
Otp: otpParam.Otp,
|
||||
Email: otpParam.Email,
|
||||
ExpiresAt: otpParam.ExpiresAt,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
}
|
||||
shouldCreate = true
|
||||
} else {
|
||||
otp.Otp = otpParam.Otp
|
||||
otp.ExpiresAt = otpParam.ExpiresAt
|
||||
}
|
||||
|
||||
otp.UpdatedAt = time.Now().Unix()
|
||||
otpCollection, _ := p.db.Collection(ctx, models.Collections.OTP)
|
||||
|
||||
var meta driver.DocumentMeta
|
||||
var err error
|
||||
if shouldCreate {
|
||||
meta, err = otpCollection.CreateDocument(ctx, otp)
|
||||
} else {
|
||||
meta, err = otpCollection.UpdateDocument(ctx, otp.Key, otp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
otp.Key = meta.Key
|
||||
otp.ID = meta.ID.String()
|
||||
return otp, nil
|
||||
}
|
||||
|
||||
// GetOTPByEmail to get otp for a given email address
|
||||
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
|
||||
var otp models.OTP
|
||||
query := fmt.Sprintf("FOR d in %s FILTER d.email == @email RETURN d", models.Collections.OTP)
|
||||
bindVars := map[string]interface{}{
|
||||
"email": emailAddress,
|
||||
}
|
||||
|
||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cursor.Close()
|
||||
|
||||
for {
|
||||
if !cursor.HasMore() {
|
||||
if otp.Key == "" {
|
||||
return nil, fmt.Errorf("email template not found")
|
||||
}
|
||||
break
|
||||
}
|
||||
_, err := cursor.ReadDocument(ctx, &otp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &otp, nil
|
||||
}
|
||||
|
||||
// DeleteOTP to delete otp
|
||||
func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
|
||||
otpCollection, _ := p.db.Collection(ctx, models.Collections.OTP)
|
||||
_, err := otpCollection.RemoveDocument(ctx, otp.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -148,6 +148,20 @@ func NewProvider() (*provider, error) {
|
|||
Sparse: true,
|
||||
})
|
||||
|
||||
otpCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.OTP)
|
||||
if !otpCollectionExists {
|
||||
_, err = arangodb.CreateCollection(ctx, models.Collections.OTP, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
otpCollection, _ := arangodb.Collection(nil, models.Collections.OTP)
|
||||
otpCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
|
||||
Unique: true,
|
||||
Sparse: true,
|
||||
})
|
||||
|
||||
return &provider{
|
||||
db: arangodb,
|
||||
}, err
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
func (p *provider) AddSession(ctx context.Context, session models.Session) error {
|
||||
if session.ID == "" {
|
||||
session.ID = uuid.New().String()
|
||||
session.Key = session.ID
|
||||
}
|
||||
|
||||
session.CreatedAt = time.Now().Unix()
|
||||
|
|
|
@ -2,22 +2,26 @@ package arangodb
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver"
|
||||
arangoDriver "github.com/arangodb/go-driver"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// AddUser to save user information in database
|
||||
func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, error) {
|
||||
if user.ID == "" {
|
||||
user.ID = uuid.New().String()
|
||||
user.Key = user.ID
|
||||
}
|
||||
|
||||
if user.Roles == "" {
|
||||
|
@ -65,7 +69,7 @@ func (p *provider) DeleteUser(ctx context.Context, user models.User) error {
|
|||
|
||||
query := fmt.Sprintf(`FOR d IN %s FILTER d.user_id == @user_id REMOVE { _key: d._key } IN %s`, models.Collections.Session, models.Collections.Session)
|
||||
bindVars := map[string]interface{}{
|
||||
"user_id": user.ID,
|
||||
"user_id": user.Key,
|
||||
}
|
||||
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||
if err != nil {
|
||||
|
@ -174,3 +178,36 @@ func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, err
|
|||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUsers to update multiple users, with parameters of user IDs slice
|
||||
// If ids set to nil / empty all the users will be updated
|
||||
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
|
||||
// set updated_at time for all users
|
||||
data["updated_at"] = time.Now().Unix()
|
||||
|
||||
userInfoBytes, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := ""
|
||||
if ids != nil && len(ids) > 0 {
|
||||
keysArray := ""
|
||||
for _, id := range ids {
|
||||
keysArray += fmt.Sprintf("'%s', ", id)
|
||||
}
|
||||
keysArray = strings.Trim(keysArray, " ")
|
||||
keysArray = strings.TrimSuffix(keysArray, ",")
|
||||
query = fmt.Sprintf("FOR u IN %s FILTER u._id IN [%s] UPDATE u._key with %s IN %s", models.Collections.User, keysArray, string(userInfoBytes), models.Collections.User)
|
||||
} else {
|
||||
query = fmt.Sprintf("FOR u IN %s UPDATE u._key with %s IN %s", models.Collections.User, string(userInfoBytes), models.Collections.User)
|
||||
}
|
||||
|
||||
_, err = p.db.Query(ctx, query, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
func (p *provider) AddVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) (models.VerificationRequest, error) {
|
||||
if verificationRequest.ID == "" {
|
||||
verificationRequest.ID = uuid.New().String()
|
||||
verificationRequest.Key = verificationRequest.ID
|
||||
}
|
||||
|
||||
verificationRequest.CreatedAt = time.Now().Unix()
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
func (p *provider) AddWebhook(ctx context.Context, webhook models.Webhook) (*model.Webhook, error) {
|
||||
if webhook.ID == "" {
|
||||
webhook.ID = uuid.New().String()
|
||||
webhook.Key = webhook.ID
|
||||
}
|
||||
|
||||
webhook.Key = webhook.ID
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
func (p *provider) AddWebhookLog(ctx context.Context, webhookLog models.WebhookLog) (*model.WebhookLog, error) {
|
||||
if webhookLog.ID == "" {
|
||||
webhookLog.ID = uuid.New().String()
|
||||
webhookLog.Key = webhookLog.ID
|
||||
}
|
||||
|
||||
webhookLog.Key = webhookLog.ID
|
||||
|
|
67
server/db/providers/cassandradb/otp.go
Normal file
67
server/db/providers/cassandradb/otp.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package cassandradb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/gocql/gocql"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// UpsertOTP to add or update otp
|
||||
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
|
||||
otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
|
||||
shouldCreate := false
|
||||
if otp == nil {
|
||||
shouldCreate = true
|
||||
otp = &models.OTP{
|
||||
ID: uuid.NewString(),
|
||||
Otp: otpParam.Otp,
|
||||
Email: otpParam.Email,
|
||||
ExpiresAt: otpParam.ExpiresAt,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
UpdatedAt: time.Now().Unix(),
|
||||
}
|
||||
} else {
|
||||
otp.Otp = otpParam.Otp
|
||||
otp.ExpiresAt = otpParam.ExpiresAt
|
||||
}
|
||||
|
||||
otp.UpdatedAt = time.Now().Unix()
|
||||
query := ""
|
||||
if shouldCreate {
|
||||
query = fmt.Sprintf(`INSERT INTO %s (id, email, otp, expires_at, created_at, updated_at) VALUES ('%s', '%s', '%s', %d, %d, %d)`, KeySpace+"."+models.Collections.OTP, otp.ID, otp.Email, otp.Otp, otp.ExpiresAt, otp.CreatedAt, otp.UpdatedAt)
|
||||
} else {
|
||||
query = fmt.Sprintf(`UPDATE %s SET otp = '%s', expires_at = %d, updated_at = %d WHERE id = '%s'`, KeySpace+"."+models.Collections.OTP, otp.Otp, otp.ExpiresAt, otp.UpdatedAt, otp.ID)
|
||||
}
|
||||
|
||||
err := p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return otp, nil
|
||||
}
|
||||
|
||||
// GetOTPByEmail to get otp for a given email address
|
||||
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
|
||||
var otp models.OTP
|
||||
query := fmt.Sprintf(`SELECT id, email, otp, expires_at, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.OTP, emailAddress)
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&otp.ID, &otp.Email, &otp.Otp, &otp.ExpiresAt, &otp.CreatedAt, &otp.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &otp, nil
|
||||
}
|
||||
|
||||
// DeleteOTP to delete otp
|
||||
func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE id = '%s'", KeySpace+"."+models.Collections.OTP, otp.ID)
|
||||
err := p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/gocql/gocql"
|
||||
cansandraDriver "github.com/gocql/gocql"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
|
@ -99,6 +100,7 @@ func NewProvider() (*provider, error) {
|
|||
cassandraClient.Consistency = gocql.LocalQuorum
|
||||
cassandraClient.ConnectTimeout = 10 * time.Second
|
||||
cassandraClient.ProtoVersion = 4
|
||||
cassandraClient.Timeout = 30 * time.Minute // for large data
|
||||
|
||||
session, err := cassandraClient.CreateSession()
|
||||
if err != nil {
|
||||
|
@ -159,6 +161,13 @@ func NewProvider() (*provider, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// add is_multi_factor_auth_enabled on users table
|
||||
userTableAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD is_multi_factor_auth_enabled boolean`, KeySpace, models.Collections.User)
|
||||
err = session.Query(userTableAlterQuery).Exec()
|
||||
if err != nil {
|
||||
log.Debug("Failed to alter table as column exists: ", err)
|
||||
// return nil, err
|
||||
}
|
||||
|
||||
// token is reserved keyword in cassandra, hence we need to use jwt_token
|
||||
verificationRequestCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, jwt_token text, identifier text, expires_at bigint, email text, nonce text, redirect_uri text, created_at bigint, updated_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.VerificationRequest)
|
||||
|
@ -221,6 +230,17 @@ func NewProvider() (*provider, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
otpCollection := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, email text, otp text, expires_at bigint, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.OTP)
|
||||
err = session.Query(otpCollection).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
otpIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_otp_email ON %s.%s (email)", KeySpace, models.Collections.OTP)
|
||||
err = session.Query(otpIndexQuery).Exec()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &provider{
|
||||
db: session,
|
||||
}, err
|
||||
|
|
|
@ -107,7 +107,7 @@ func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.Use
|
|||
}
|
||||
|
||||
if value == nil {
|
||||
updateFields += fmt.Sprintf("%s = null,", key)
|
||||
updateFields += fmt.Sprintf("%s = null, ", key)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,6 @@ func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.Use
|
|||
updateFields = strings.TrimSuffix(updateFields, ",")
|
||||
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE id = '%s'", KeySpace+"."+models.Collections.User, updateFields, user.ID)
|
||||
|
||||
err = p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return user, err
|
||||
|
@ -173,14 +172,14 @@ func (p *provider) ListUsers(ctx context.Context, pagination model.Pagination) (
|
|||
// there is no offset in cassandra
|
||||
// so we fetch till limit + offset
|
||||
// and return the results from offset to limit
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User, pagination.Limit+pagination.Offset)
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User, pagination.Limit+pagination.Offset)
|
||||
|
||||
scanner := p.db.Query(query).Iter().Scanner()
|
||||
counter := int64(0)
|
||||
for scanner.Next() {
|
||||
if counter >= pagination.Offset {
|
||||
var user models.User
|
||||
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
|
||||
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -197,8 +196,8 @@ func (p *provider) ListUsers(ctx context.Context, pagination model.Pagination) (
|
|||
// GetUserByEmail to get user information from database using email address
|
||||
func (p *provider) GetUserByEmail(ctx context.Context, email string) (models.User, error) {
|
||||
var user models.User
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, email)
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, email)
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
@ -208,10 +207,95 @@ func (p *provider) GetUserByEmail(ctx context.Context, email string) (models.Use
|
|||
// GetUserByID to get user information from database using user ID
|
||||
func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, error) {
|
||||
var user models.User
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, id)
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.CreatedAt, &user.UpdatedAt)
|
||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, id)
|
||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUsers to update multiple users, with parameters of user IDs slice
|
||||
// If ids set to nil / empty all the users will be updated
|
||||
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
|
||||
// set updated_at time for all users
|
||||
data["updated_at"] = time.Now().Unix()
|
||||
|
||||
updateFields := ""
|
||||
for key, value := range data {
|
||||
if key == "_id" {
|
||||
continue
|
||||
}
|
||||
|
||||
if key == "_key" {
|
||||
continue
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
updateFields += fmt.Sprintf("%s = null,", key)
|
||||
continue
|
||||
}
|
||||
|
||||
valueType := reflect.TypeOf(value)
|
||||
if valueType.Name() == "string" {
|
||||
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
|
||||
} else {
|
||||
updateFields += fmt.Sprintf("%s = %v, ", key, value)
|
||||
}
|
||||
}
|
||||
updateFields = strings.Trim(updateFields, " ")
|
||||
updateFields = strings.TrimSuffix(updateFields, ",")
|
||||
|
||||
query := ""
|
||||
if ids != nil && len(ids) > 0 {
|
||||
idsString := ""
|
||||
for _, id := range ids {
|
||||
idsString += fmt.Sprintf("'%s', ", id)
|
||||
}
|
||||
idsString = strings.Trim(idsString, " ")
|
||||
idsString = strings.TrimSuffix(idsString, ",")
|
||||
query = fmt.Sprintf("UPDATE %s SET %s WHERE id IN (%s)", KeySpace+"."+models.Collections.User, updateFields, idsString)
|
||||
err := p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// get all ids
|
||||
getUserIDsQuery := fmt.Sprintf(`SELECT id FROM %s`, KeySpace+"."+models.Collections.User)
|
||||
scanner := p.db.Query(getUserIDsQuery).Iter().Scanner()
|
||||
// only 100 ids are allowed in 1 query
|
||||
// hence we need create multiple update queries
|
||||
idsString := ""
|
||||
idsStringArray := []string{idsString}
|
||||
counter := 1
|
||||
for scanner.Next() {
|
||||
var id string
|
||||
err := scanner.Scan(&id)
|
||||
if err == nil {
|
||||
idsString += fmt.Sprintf("'%s', ", id)
|
||||
}
|
||||
counter++
|
||||
if counter > 100 {
|
||||
idsStringArray = append(idsStringArray, idsString)
|
||||
counter = 1
|
||||
idsString = ""
|
||||
} else {
|
||||
// update the last index of array when count is less than 100
|
||||
idsStringArray[len(idsStringArray)-1] = idsString
|
||||
}
|
||||
}
|
||||
|
||||
for _, idStr := range idsStringArray {
|
||||
idStr = strings.Trim(idStr, " ")
|
||||
idStr = strings.TrimSuffix(idStr, ",")
|
||||
query = fmt.Sprintf("UPDATE %s SET %s WHERE id IN (%s)", KeySpace+"."+models.Collections.User, updateFields, idStr)
|
||||
err := p.db.Query(query).Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
70
server/db/providers/mongodb/otp.go
Normal file
70
server/db/providers/mongodb/otp.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package mongodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/google/uuid"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// UpsertOTP to add or update otp
|
||||
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
|
||||
otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
|
||||
shouldCreate := false
|
||||
if otp == nil {
|
||||
id := uuid.NewString()
|
||||
otp = &models.OTP{
|
||||
ID: id,
|
||||
Key: id,
|
||||
Otp: otpParam.Otp,
|
||||
Email: otpParam.Email,
|
||||
ExpiresAt: otpParam.ExpiresAt,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
}
|
||||
shouldCreate = true
|
||||
} else {
|
||||
otp.Otp = otpParam.Otp
|
||||
otp.ExpiresAt = otpParam.ExpiresAt
|
||||
}
|
||||
otp.UpdatedAt = time.Now().Unix()
|
||||
otpCollection := p.db.Collection(models.Collections.OTP, options.Collection())
|
||||
|
||||
var err error
|
||||
if shouldCreate {
|
||||
_, err = otpCollection.InsertOne(ctx, otp)
|
||||
} else {
|
||||
_, err = otpCollection.UpdateOne(ctx, bson.M{"_id": bson.M{"$eq": otp.ID}}, bson.M{"$set": otp}, options.MergeUpdateOptions())
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return otp, nil
|
||||
}
|
||||
|
||||
// GetOTPByEmail to get otp for a given email address
|
||||
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
|
||||
var otp models.OTP
|
||||
|
||||
otpCollection := p.db.Collection(models.Collections.OTP, options.Collection())
|
||||
err := otpCollection.FindOne(ctx, bson.M{"email": emailAddress}).Decode(&otp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &otp, nil
|
||||
}
|
||||
|
||||
// DeleteOTP to delete otp
|
||||
func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
|
||||
otpCollection := p.db.Collection(models.Collections.OTP, options.Collection())
|
||||
_, err := otpCollection.DeleteOne(nil, bson.M{"_id": otp.ID}, options.Delete())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -110,6 +110,15 @@ func NewProvider() (*provider, error) {
|
|||
},
|
||||
}, options.CreateIndexes())
|
||||
|
||||
mongodb.CreateCollection(ctx, models.Collections.OTP, options.CreateCollection())
|
||||
otpCollection := mongodb.Collection(models.Collections.OTP, options.Collection())
|
||||
otpCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.M{"email": 1},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
}, options.CreateIndexes())
|
||||
|
||||
return &provider{
|
||||
db: mongodb,
|
||||
}, nil
|
||||
|
|
|
@ -9,7 +9,9 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
|
@ -129,3 +131,27 @@ func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, err
|
|||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUsers to update multiple users, with parameters of user IDs slice
|
||||
// If ids set to nil / empty all the users will be updated
|
||||
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
|
||||
// set updated_at time for all users
|
||||
data["updated_at"] = time.Now().Unix()
|
||||
|
||||
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
||||
|
||||
var res *mongo.UpdateResult
|
||||
var err error
|
||||
if ids != nil && len(ids) > 0 {
|
||||
res, err = userCollection.UpdateMany(ctx, bson.M{"_id": bson.M{"$in": ids}}, bson.M{"$set": data})
|
||||
} else {
|
||||
res, err = userCollection.UpdateMany(ctx, bson.M{}, bson.M{"$set": data})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
log.Info("Updated users: ", res.ModifiedCount)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
22
server/db/providers/provider_template/otp.go
Normal file
22
server/db/providers/provider_template/otp.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package provider_template
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
)
|
||||
|
||||
// UpsertOTP to add or update otp
|
||||
func (p *provider) UpsertOTP(ctx context.Context, otp *models.OTP) (*models.OTP, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetOTPByEmail to get otp for a given email address
|
||||
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// DeleteOTP to delete otp
|
||||
func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
|
||||
return nil
|
||||
}
|
|
@ -60,3 +60,12 @@ func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, err
|
|||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUsers to update multiple users, with parameters of user IDs slice
|
||||
// If ids set to nil / empty all the users will be updated
|
||||
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
|
||||
// set updated_at time for all users
|
||||
data["updated_at"] = time.Now().Unix()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@ type Provider interface {
|
|||
GetUserByEmail(ctx context.Context, email string) (models.User, error)
|
||||
// GetUserByID to get user information from database using user ID
|
||||
GetUserByID(ctx context.Context, id string) (models.User, error)
|
||||
// UpdateUsers to update multiple users, with parameters of user IDs slice
|
||||
// If ids set to nil / empty all the users will be updated
|
||||
UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error
|
||||
|
||||
// AddVerification to save verification request in database
|
||||
AddVerificationRequest(ctx context.Context, verificationRequest models.VerificationRequest) (models.VerificationRequest, error)
|
||||
|
@ -72,4 +75,11 @@ type Provider interface {
|
|||
GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error)
|
||||
// DeleteEmailTemplate to delete EmailTemplate
|
||||
DeleteEmailTemplate(ctx context.Context, emailTemplate *model.EmailTemplate) error
|
||||
|
||||
// UpsertOTP to add or update otp
|
||||
UpsertOTP(ctx context.Context, otp *models.OTP) (*models.OTP, error)
|
||||
// GetOTPByEmail to get otp for a given email address
|
||||
GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error)
|
||||
// DeleteOTP to delete otp
|
||||
DeleteOTP(ctx context.Context, otp *models.OTP) error
|
||||
}
|
||||
|
|
53
server/db/providers/sql/otp.go
Normal file
53
server/db/providers/sql/otp.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// UpsertOTP to add or update otp
|
||||
func (p *provider) UpsertOTP(ctx context.Context, otp *models.OTP) (*models.OTP, error) {
|
||||
if otp.ID == "" {
|
||||
otp.ID = uuid.New().String()
|
||||
}
|
||||
|
||||
otp.Key = otp.ID
|
||||
otp.CreatedAt = time.Now().Unix()
|
||||
otp.UpdatedAt = time.Now().Unix()
|
||||
|
||||
res := p.db.Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "email"}},
|
||||
DoUpdates: clause.AssignmentColumns([]string{"otp", "expires_at", "updated_at"}),
|
||||
}).Create(&otp)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
return otp, nil
|
||||
}
|
||||
|
||||
// GetOTPByEmail to get otp for a given email address
|
||||
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
|
||||
var otp models.OTP
|
||||
|
||||
result := p.db.Where("email = ?", emailAddress).First(&otp)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
return &otp, nil
|
||||
}
|
||||
|
||||
// DeleteOTP to delete otp
|
||||
func (p *provider) DeleteOTP(ctx context.Context, otp *models.OTP) error {
|
||||
result := p.db.Delete(&models.OTP{
|
||||
ID: otp.ID,
|
||||
})
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -40,6 +40,7 @@ func NewProvider() (*provider, error) {
|
|||
NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: models.Prefix,
|
||||
},
|
||||
AllowGlobalUpdate: true,
|
||||
}
|
||||
|
||||
dbType := memorystore.RequiredEnvStoreObj.GetRequiredEnv().DatabaseType
|
||||
|
@ -60,7 +61,7 @@ func NewProvider() (*provider, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{}, &models.Webhook{}, models.WebhookLog{}, models.EmailTemplate{})
|
||||
err = sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{}, &models.Webhook{}, models.WebhookLog{}, models.EmailTemplate{}, &models.OTP{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
|
@ -121,3 +122,22 @@ func (p *provider) GetUserByID(ctx context.Context, id string) (models.User, err
|
|||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// UpdateUsers to update multiple users, with parameters of user IDs slice
|
||||
// If ids set to nil / empty all the users will be updated
|
||||
func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error {
|
||||
// set updated_at time for all users
|
||||
data["updated_at"] = time.Now().Unix()
|
||||
|
||||
var res *gorm.DB
|
||||
if ids != nil && len(ids) > 0 {
|
||||
res = p.db.Model(&models.User{}).Where("id in ?", ids).Updates(data)
|
||||
} else {
|
||||
res = p.db.Model(&models.User{}).Updates(data)
|
||||
}
|
||||
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
118
server/email/otp.go
Normal file
118
server/email/otp.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
package email
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
)
|
||||
|
||||
// SendOtpMail to send otp email
|
||||
func SendOtpMail(toEmail, otp string) error {
|
||||
// The receiver needs to be in slice as the receive supports multiple receiver
|
||||
Receiver := []string{toEmail}
|
||||
|
||||
Subject := "OTP for your multi factor authentication"
|
||||
message := `
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport">
|
||||
<meta name="x-apple-disable-message-reformatting">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta content="telephone=no" name="format-detection">
|
||||
<title></title>
|
||||
<!--[if (mso 16)]>
|
||||
<style type="text/css">
|
||||
a {}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if gte mso 9]><style>sup { font-size: 100%% !important; }</style><![endif]-->
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG></o:AllowPNG>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body style="font-family: sans-serif;">
|
||||
<div class="es-wrapper-color">
|
||||
<!--[if gte mso 9]>
|
||||
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="t">
|
||||
<v:fill type="tile" color="#ffffff"></v:fill>
|
||||
</v:background>
|
||||
<![endif]-->
|
||||
<table class="es-wrapper" width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-email-paddings" valign="top">
|
||||
<table class="es-content esd-footer-popover" cellspacing="0" cellpadding="0" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-stripe" align="center">
|
||||
<table class="es-content-body" style="border-left:1px solid transparent;border-right:1px solid transparent;border-top:1px solid transparent;border-bottom:1px solid transparent;padding:20px 0px;" width="600" cellspacing="0" cellpadding="0" bgcolor="#ffffff" align="center">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-structure es-p20t es-p40b es-p40r es-p40l" esd-custom-block-id="8537" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-container-frame" width="518" align="left">
|
||||
<table width="100%%" cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="esd-block-image es-m-txt-c es-p5b" style="font-size:0;padding:10px" align="center"><a target="_blank" clicktracking="off"><img src="{{.org_logo}}" alt="icon" style="display: block;" title="icon" width="30"></a></td>
|
||||
</tr>
|
||||
|
||||
<tr style="background: rgb(249,250,251);padding: 10px;margin-bottom:10px;border-radius:5px;">
|
||||
<td class="esd-block-text es-m-txt-c es-p15t" align="center" style="padding:10px;padding-bottom:30px;">
|
||||
<p>Hey there 👋</p>
|
||||
<b>{{.otp}}</b> is your one time password (OTP) for accessing {{.org_name}}. Please keep your OTP confidential and it will expire in 1 minute.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="position: absolute; left: -9999px; top: -9999px; margin: 0px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
data := make(map[string]interface{}, 3)
|
||||
var err error
|
||||
data["org_logo"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data["org_name"], err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data["otp"] = otp
|
||||
message = addEmailTemplate(message, data, "otp.tmpl")
|
||||
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||
|
||||
err = SendMail(Receiver, Subject, message)
|
||||
if err != nil {
|
||||
log.Warn("error sending email: ", err)
|
||||
}
|
||||
return err
|
||||
}
|
41
server/env/env.go
vendored
41
server/env/env.go
vendored
|
@ -84,6 +84,8 @@ func InitAllEnv() error {
|
|||
osDisableSignUp := os.Getenv(constants.EnvKeyDisableSignUp)
|
||||
osDisableRedisForEnv := os.Getenv(constants.EnvKeyDisableRedisForEnv)
|
||||
osDisableStrongPassword := os.Getenv(constants.EnvKeyDisableStrongPassword)
|
||||
osEnforceMultiFactorAuthentication := os.Getenv(constants.EnvKeyEnforceMultiFactorAuthentication)
|
||||
osDisableMultiFactorAuthentication := os.Getenv(constants.EnvKeyDisableMultiFactorAuthentication)
|
||||
|
||||
// os slice vars
|
||||
osAllowedOrigins := os.Getenv(constants.EnvKeyAllowedOrigins)
|
||||
|
@ -490,10 +492,49 @@ func InitAllEnv() error {
|
|||
}
|
||||
}
|
||||
|
||||
if _, ok := envData[constants.EnvKeyEnforceMultiFactorAuthentication]; !ok {
|
||||
envData[constants.EnvKeyEnforceMultiFactorAuthentication] = osEnforceMultiFactorAuthentication == "true"
|
||||
}
|
||||
if osEnforceMultiFactorAuthentication != "" {
|
||||
boolValue, err := strconv.ParseBool(osEnforceMultiFactorAuthentication)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boolValue != envData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) {
|
||||
envData[constants.EnvKeyEnforceMultiFactorAuthentication] = boolValue
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := envData[constants.EnvKeyDisableMultiFactorAuthentication]; !ok {
|
||||
envData[constants.EnvKeyDisableMultiFactorAuthentication] = osDisableMultiFactorAuthentication == "true"
|
||||
}
|
||||
if osDisableMultiFactorAuthentication != "" {
|
||||
boolValue, err := strconv.ParseBool(osDisableMultiFactorAuthentication)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if boolValue != envData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) {
|
||||
envData[constants.EnvKeyDisableMultiFactorAuthentication] = boolValue
|
||||
}
|
||||
}
|
||||
|
||||
// no need to add nil check as its already done above
|
||||
if envData[constants.EnvKeySmtpHost] == "" || envData[constants.EnvKeySmtpUsername] == "" || envData[constants.EnvKeySmtpPassword] == "" || envData[constants.EnvKeySenderEmail] == "" && envData[constants.EnvKeySmtpPort] == "" {
|
||||
envData[constants.EnvKeyDisableEmailVerification] = true
|
||||
envData[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||
envData[constants.EnvKeyIsEmailServiceEnabled] = false
|
||||
}
|
||||
|
||||
if envData[constants.EnvKeySmtpHost] != "" || envData[constants.EnvKeySmtpUsername] != "" || envData[constants.EnvKeySmtpPassword] != "" || envData[constants.EnvKeySenderEmail] != "" && envData[constants.EnvKeySmtpPort] != "" {
|
||||
envData[constants.EnvKeyIsEmailServiceEnabled] = true
|
||||
}
|
||||
|
||||
if envData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && !envData[constants.EnvKeyIsEmailServiceEnabled].(bool) {
|
||||
return errors.New("to enable multi factor authentication, please enable email service")
|
||||
}
|
||||
|
||||
if !envData[constants.EnvKeyIsEmailServiceEnabled].(bool) {
|
||||
envData[constants.EnvKeyDisableMultiFactorAuthentication] = true
|
||||
}
|
||||
|
||||
if envData[constants.EnvKeyDisableEmailVerification].(bool) {
|
||||
|
|
4
server/env/persist_env.go
vendored
4
server/env/persist_env.go
vendored
|
@ -201,7 +201,7 @@ func PersistEnv() error {
|
|||
envValue := strings.TrimSpace(os.Getenv(key))
|
||||
if envValue != "" {
|
||||
switch key {
|
||||
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword:
|
||||
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication:
|
||||
if envValueBool, err := strconv.ParseBool(envValue); err == nil {
|
||||
if value.(bool) != envValueBool {
|
||||
storeData[key] = envValueBool
|
||||
|
@ -221,6 +221,8 @@ func PersistEnv() error {
|
|||
// handle derivative cases like disabling email verification & magic login
|
||||
// in case SMTP is off but env is set to true
|
||||
if storeData[constants.EnvKeySmtpHost] == "" || storeData[constants.EnvKeySmtpUsername] == "" || storeData[constants.EnvKeySmtpPassword] == "" || storeData[constants.EnvKeySenderEmail] == "" && storeData[constants.EnvKeySmtpPort] == "" {
|
||||
storeData[constants.EnvKeyIsEmailServiceEnabled] = false
|
||||
|
||||
if !storeData[constants.EnvKeyDisableEmailVerification].(bool) {
|
||||
storeData[constants.EnvKeyDisableEmailVerification] = true
|
||||
hasChanged = true
|
||||
|
|
|
@ -9,7 +9,7 @@ require (
|
|||
github.com/gin-gonic/gin v1.7.2
|
||||
github.com/go-playground/validator/v10 v10.8.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.0
|
||||
github.com/gocql/gocql v1.0.0
|
||||
github.com/gocql/gocql v1.2.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
|
|
|
@ -110,8 +110,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC
|
|||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
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/gocql/gocql v1.0.0 h1:UnbTERpP72VZ/viKE1Q1gPtmLvyTZTvuAstvSRydw/c=
|
||||
github.com/gocql/gocql v1.0.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
|
||||
github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE=
|
||||
github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
|
||||
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.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
|
|
|
@ -44,12 +44,13 @@ type DirectiveRoot struct {
|
|||
|
||||
type ComplexityRoot struct {
|
||||
AuthResponse struct {
|
||||
AccessToken func(childComplexity int) int
|
||||
ExpiresIn func(childComplexity int) int
|
||||
IDToken func(childComplexity int) int
|
||||
Message func(childComplexity int) int
|
||||
RefreshToken func(childComplexity int) int
|
||||
User func(childComplexity int) int
|
||||
AccessToken func(childComplexity int) int
|
||||
ExpiresIn func(childComplexity int) int
|
||||
IDToken func(childComplexity int) int
|
||||
Message func(childComplexity int) int
|
||||
RefreshToken func(childComplexity int) int
|
||||
ShouldShowOtpScreen func(childComplexity int) int
|
||||
User func(childComplexity int) int
|
||||
}
|
||||
|
||||
EmailTemplate struct {
|
||||
|
@ -67,54 +68,56 @@ type ComplexityRoot struct {
|
|||
}
|
||||
|
||||
Env struct {
|
||||
AccessTokenExpiryTime func(childComplexity int) int
|
||||
AdminSecret func(childComplexity int) int
|
||||
AllowedOrigins func(childComplexity int) int
|
||||
AppURL func(childComplexity int) int
|
||||
AppleClientID func(childComplexity int) int
|
||||
AppleClientSecret func(childComplexity int) int
|
||||
ClientID func(childComplexity int) int
|
||||
ClientSecret func(childComplexity int) int
|
||||
CustomAccessTokenScript func(childComplexity int) int
|
||||
DatabaseHost func(childComplexity int) int
|
||||
DatabaseName func(childComplexity int) int
|
||||
DatabasePassword func(childComplexity int) int
|
||||
DatabasePort func(childComplexity int) int
|
||||
DatabaseType func(childComplexity int) int
|
||||
DatabaseURL func(childComplexity int) int
|
||||
DatabaseUsername func(childComplexity int) int
|
||||
DefaultRoles func(childComplexity int) int
|
||||
DisableBasicAuthentication func(childComplexity int) int
|
||||
DisableEmailVerification func(childComplexity int) int
|
||||
DisableLoginPage func(childComplexity int) int
|
||||
DisableMagicLinkLogin func(childComplexity int) int
|
||||
DisableRedisForEnv func(childComplexity int) int
|
||||
DisableSignUp func(childComplexity int) int
|
||||
DisableStrongPassword func(childComplexity int) int
|
||||
FacebookClientID func(childComplexity int) int
|
||||
FacebookClientSecret func(childComplexity int) int
|
||||
GithubClientID func(childComplexity int) int
|
||||
GithubClientSecret func(childComplexity int) int
|
||||
GoogleClientID func(childComplexity int) int
|
||||
GoogleClientSecret func(childComplexity int) int
|
||||
JwtPrivateKey func(childComplexity int) int
|
||||
JwtPublicKey func(childComplexity int) int
|
||||
JwtRoleClaim func(childComplexity int) int
|
||||
JwtSecret func(childComplexity int) int
|
||||
JwtType func(childComplexity int) int
|
||||
LinkedinClientID func(childComplexity int) int
|
||||
LinkedinClientSecret func(childComplexity int) int
|
||||
OrganizationLogo func(childComplexity int) int
|
||||
OrganizationName func(childComplexity int) int
|
||||
ProtectedRoles func(childComplexity int) int
|
||||
RedisURL func(childComplexity int) int
|
||||
ResetPasswordURL func(childComplexity int) int
|
||||
Roles func(childComplexity int) int
|
||||
SMTPHost func(childComplexity int) int
|
||||
SMTPPassword func(childComplexity int) int
|
||||
SMTPPort func(childComplexity int) int
|
||||
SMTPUsername func(childComplexity int) int
|
||||
SenderEmail func(childComplexity int) int
|
||||
AccessTokenExpiryTime func(childComplexity int) int
|
||||
AdminSecret func(childComplexity int) int
|
||||
AllowedOrigins func(childComplexity int) int
|
||||
AppURL func(childComplexity int) int
|
||||
AppleClientID func(childComplexity int) int
|
||||
AppleClientSecret func(childComplexity int) int
|
||||
ClientID func(childComplexity int) int
|
||||
ClientSecret func(childComplexity int) int
|
||||
CustomAccessTokenScript func(childComplexity int) int
|
||||
DatabaseHost func(childComplexity int) int
|
||||
DatabaseName func(childComplexity int) int
|
||||
DatabasePassword func(childComplexity int) int
|
||||
DatabasePort func(childComplexity int) int
|
||||
DatabaseType func(childComplexity int) int
|
||||
DatabaseURL func(childComplexity int) int
|
||||
DatabaseUsername func(childComplexity int) int
|
||||
DefaultRoles func(childComplexity int) int
|
||||
DisableBasicAuthentication func(childComplexity int) int
|
||||
DisableEmailVerification func(childComplexity int) int
|
||||
DisableLoginPage func(childComplexity int) int
|
||||
DisableMagicLinkLogin func(childComplexity int) int
|
||||
DisableMultiFactorAuthentication func(childComplexity int) int
|
||||
DisableRedisForEnv func(childComplexity int) int
|
||||
DisableSignUp func(childComplexity int) int
|
||||
DisableStrongPassword func(childComplexity int) int
|
||||
EnforceMultiFactorAuthentication func(childComplexity int) int
|
||||
FacebookClientID func(childComplexity int) int
|
||||
FacebookClientSecret func(childComplexity int) int
|
||||
GithubClientID func(childComplexity int) int
|
||||
GithubClientSecret func(childComplexity int) int
|
||||
GoogleClientID func(childComplexity int) int
|
||||
GoogleClientSecret func(childComplexity int) int
|
||||
JwtPrivateKey func(childComplexity int) int
|
||||
JwtPublicKey func(childComplexity int) int
|
||||
JwtRoleClaim func(childComplexity int) int
|
||||
JwtSecret func(childComplexity int) int
|
||||
JwtType func(childComplexity int) int
|
||||
LinkedinClientID func(childComplexity int) int
|
||||
LinkedinClientSecret func(childComplexity int) int
|
||||
OrganizationLogo func(childComplexity int) int
|
||||
OrganizationName func(childComplexity int) int
|
||||
ProtectedRoles func(childComplexity int) int
|
||||
RedisURL func(childComplexity int) int
|
||||
ResetPasswordURL func(childComplexity int) int
|
||||
Roles func(childComplexity int) int
|
||||
SMTPHost func(childComplexity int) int
|
||||
SMTPPassword func(childComplexity int) int
|
||||
SMTPPort func(childComplexity int) int
|
||||
SMTPUsername func(childComplexity int) int
|
||||
SenderEmail func(childComplexity int) int
|
||||
}
|
||||
|
||||
Error struct {
|
||||
|
@ -138,6 +141,7 @@ type ComplexityRoot struct {
|
|||
IsGoogleLoginEnabled func(childComplexity int) int
|
||||
IsLinkedinLoginEnabled func(childComplexity int) int
|
||||
IsMagicLinkLoginEnabled func(childComplexity int) int
|
||||
IsMultiFactorAuthEnabled func(childComplexity int) int
|
||||
IsSignUpEnabled func(childComplexity int) int
|
||||
IsStrongPasswordEnabled func(childComplexity int) int
|
||||
Version func(childComplexity int) int
|
||||
|
@ -159,6 +163,7 @@ type ComplexityRoot struct {
|
|||
Login func(childComplexity int, params model.LoginInput) int
|
||||
Logout func(childComplexity int) int
|
||||
MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int
|
||||
ResendOtp func(childComplexity int, params model.ResendOTPRequest) int
|
||||
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
||||
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
||||
Revoke func(childComplexity int, params model.OAuthRevokeInput) int
|
||||
|
@ -171,6 +176,7 @@ type ComplexityRoot struct {
|
|||
UpdateUser func(childComplexity int, params model.UpdateUserInput) int
|
||||
UpdateWebhook func(childComplexity int, params model.UpdateWebhookRequest) int
|
||||
VerifyEmail func(childComplexity int, params model.VerifyEmailInput) int
|
||||
VerifyOtp func(childComplexity int, params model.VerifyOTPRequest) int
|
||||
}
|
||||
|
||||
Pagination struct {
|
||||
|
@ -205,24 +211,25 @@ type ComplexityRoot struct {
|
|||
}
|
||||
|
||||
User struct {
|
||||
Birthdate func(childComplexity int) int
|
||||
CreatedAt func(childComplexity int) int
|
||||
Email func(childComplexity int) int
|
||||
EmailVerified func(childComplexity int) int
|
||||
FamilyName func(childComplexity int) int
|
||||
Gender func(childComplexity int) int
|
||||
GivenName func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
MiddleName func(childComplexity int) int
|
||||
Nickname func(childComplexity int) int
|
||||
PhoneNumber func(childComplexity int) int
|
||||
PhoneNumberVerified func(childComplexity int) int
|
||||
Picture func(childComplexity int) int
|
||||
PreferredUsername func(childComplexity int) int
|
||||
RevokedTimestamp func(childComplexity int) int
|
||||
Roles func(childComplexity int) int
|
||||
SignupMethods func(childComplexity int) int
|
||||
UpdatedAt func(childComplexity int) int
|
||||
Birthdate func(childComplexity int) int
|
||||
CreatedAt func(childComplexity int) int
|
||||
Email func(childComplexity int) int
|
||||
EmailVerified func(childComplexity int) int
|
||||
FamilyName func(childComplexity int) int
|
||||
Gender func(childComplexity int) int
|
||||
GivenName func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
IsMultiFactorAuthEnabled func(childComplexity int) int
|
||||
MiddleName func(childComplexity int) int
|
||||
Nickname func(childComplexity int) int
|
||||
PhoneNumber func(childComplexity int) int
|
||||
PhoneNumberVerified func(childComplexity int) int
|
||||
Picture func(childComplexity int) int
|
||||
PreferredUsername func(childComplexity int) int
|
||||
RevokedTimestamp func(childComplexity int) int
|
||||
Roles func(childComplexity int) int
|
||||
SignupMethods func(childComplexity int) int
|
||||
UpdatedAt func(childComplexity int) int
|
||||
}
|
||||
|
||||
Users struct {
|
||||
|
@ -293,6 +300,8 @@ type MutationResolver interface {
|
|||
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error)
|
||||
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
|
||||
Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error)
|
||||
VerifyOtp(ctx context.Context, params model.VerifyOTPRequest) (*model.AuthResponse, error)
|
||||
ResendOtp(ctx context.Context, params model.ResendOTPRequest) (*model.Response, error)
|
||||
DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error)
|
||||
UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error)
|
||||
AdminSignup(ctx context.Context, params model.AdminSignupInput) (*model.Response, error)
|
||||
|
@ -376,6 +385,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.AuthResponse.RefreshToken(childComplexity), true
|
||||
|
||||
case "AuthResponse.should_show_otp_screen":
|
||||
if e.complexity.AuthResponse.ShouldShowOtpScreen == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.AuthResponse.ShouldShowOtpScreen(childComplexity), true
|
||||
|
||||
case "AuthResponse.user":
|
||||
if e.complexity.AuthResponse.User == nil {
|
||||
break
|
||||
|
@ -586,6 +602,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Env.DisableMagicLinkLogin(childComplexity), true
|
||||
|
||||
case "Env.DISABLE_MULTI_FACTOR_AUTHENTICATION":
|
||||
if e.complexity.Env.DisableMultiFactorAuthentication == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Env.DisableMultiFactorAuthentication(childComplexity), true
|
||||
|
||||
case "Env.DISABLE_REDIS_FOR_ENV":
|
||||
if e.complexity.Env.DisableRedisForEnv == nil {
|
||||
break
|
||||
|
@ -607,6 +630,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Env.DisableStrongPassword(childComplexity), true
|
||||
|
||||
case "Env.ENFORCE_MULTI_FACTOR_AUTHENTICATION":
|
||||
if e.complexity.Env.EnforceMultiFactorAuthentication == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Env.EnforceMultiFactorAuthentication(childComplexity), true
|
||||
|
||||
case "Env.FACEBOOK_CLIENT_ID":
|
||||
if e.complexity.Env.FacebookClientID == nil {
|
||||
break
|
||||
|
@ -873,6 +903,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Meta.IsMagicLinkLoginEnabled(childComplexity), true
|
||||
|
||||
case "Meta.is_multi_factor_auth_enabled":
|
||||
if e.complexity.Meta.IsMultiFactorAuthEnabled == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Meta.IsMultiFactorAuthEnabled(childComplexity), true
|
||||
|
||||
case "Meta.is_sign_up_enabled":
|
||||
if e.complexity.Meta.IsSignUpEnabled == nil {
|
||||
break
|
||||
|
@ -1064,6 +1101,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Mutation.MagicLinkLogin(childComplexity, args["params"].(model.MagicLinkLoginInput)), true
|
||||
|
||||
case "Mutation.resend_otp":
|
||||
if e.complexity.Mutation.ResendOtp == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_resend_otp_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.ResendOtp(childComplexity, args["params"].(model.ResendOTPRequest)), true
|
||||
|
||||
case "Mutation.resend_verify_email":
|
||||
if e.complexity.Mutation.ResendVerifyEmail == nil {
|
||||
break
|
||||
|
@ -1208,6 +1257,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Mutation.VerifyEmail(childComplexity, args["params"].(model.VerifyEmailInput)), true
|
||||
|
||||
case "Mutation.verify_otp":
|
||||
if e.complexity.Mutation.VerifyOtp == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_verify_otp_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.VerifyOtp(childComplexity, args["params"].(model.VerifyOTPRequest)), true
|
||||
|
||||
case "Pagination.limit":
|
||||
if e.complexity.Pagination.Limit == nil {
|
||||
break
|
||||
|
@ -1437,6 +1498,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.User.ID(childComplexity), true
|
||||
|
||||
case "User.is_multi_factor_auth_enabled":
|
||||
if e.complexity.User.IsMultiFactorAuthEnabled == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.User.IsMultiFactorAuthEnabled(childComplexity), true
|
||||
|
||||
case "User.middle_name":
|
||||
if e.complexity.User.MiddleName == nil {
|
||||
break
|
||||
|
@ -1822,6 +1890,7 @@ type Meta {
|
|||
is_magic_link_login_enabled: Boolean!
|
||||
is_sign_up_enabled: Boolean!
|
||||
is_strong_password_enabled: Boolean!
|
||||
is_multi_factor_auth_enabled: Boolean!
|
||||
}
|
||||
|
||||
type User {
|
||||
|
@ -1844,6 +1913,7 @@ type User {
|
|||
created_at: Int64
|
||||
updated_at: Int64
|
||||
revoked_timestamp: Int64
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
}
|
||||
|
||||
type Users {
|
||||
|
@ -1875,6 +1945,7 @@ type Error {
|
|||
|
||||
type AuthResponse {
|
||||
message: String!
|
||||
should_show_otp_screen: Boolean
|
||||
access_token: String
|
||||
id_token: String
|
||||
refresh_token: String
|
||||
|
@ -1919,6 +1990,8 @@ type Env {
|
|||
DISABLE_SIGN_UP: Boolean!
|
||||
DISABLE_REDIS_FOR_ENV: Boolean!
|
||||
DISABLE_STRONG_PASSWORD: Boolean!
|
||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean!
|
||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean!
|
||||
ROLES: [String!]
|
||||
PROTECTED_ROLES: [String!]
|
||||
DEFAULT_ROLES: [String!]
|
||||
|
@ -2020,6 +2093,8 @@ input UpdateEnvInput {
|
|||
DISABLE_SIGN_UP: Boolean
|
||||
DISABLE_REDIS_FOR_ENV: Boolean
|
||||
DISABLE_STRONG_PASSWORD: Boolean
|
||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean
|
||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean
|
||||
ROLES: [String!]
|
||||
PROTECTED_ROLES: [String!]
|
||||
DEFAULT_ROLES: [String!]
|
||||
|
@ -2061,6 +2136,7 @@ input SignUpInput {
|
|||
roles: [String!]
|
||||
scope: [String!]
|
||||
redirect_uri: String
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
|
@ -2092,6 +2168,7 @@ input UpdateProfileInput {
|
|||
birthdate: String
|
||||
phone_number: String
|
||||
picture: String
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
}
|
||||
|
||||
input UpdateUserInput {
|
||||
|
@ -2107,6 +2184,7 @@ input UpdateUserInput {
|
|||
phone_number: String
|
||||
picture: String
|
||||
roles: [String]
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
}
|
||||
|
||||
input ForgotPasswordInput {
|
||||
|
@ -2217,6 +2295,15 @@ input DeleteEmailTemplateRequest {
|
|||
id: ID!
|
||||
}
|
||||
|
||||
input VerifyOTPRequest {
|
||||
email: String!
|
||||
otp: String!
|
||||
}
|
||||
|
||||
input ResendOTPRequest {
|
||||
email: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
signup(params: SignUpInput!): AuthResponse!
|
||||
login(params: LoginInput!): AuthResponse!
|
||||
|
@ -2228,6 +2315,8 @@ type Mutation {
|
|||
forgot_password(params: ForgotPasswordInput!): Response!
|
||||
reset_password(params: ResetPasswordInput!): Response!
|
||||
revoke(params: OAuthRevokeInput!): Response!
|
||||
verify_otp(params: VerifyOTPRequest!): AuthResponse!
|
||||
resend_otp(params: ResendOTPRequest!): Response!
|
||||
# admin only apis
|
||||
_delete_user(params: DeleteUserInput!): Response!
|
||||
_update_user(params: UpdateUserInput!): User!
|
||||
|
@ -2556,6 +2645,21 @@ func (ec *executionContext) field_Mutation_magic_link_login_args(ctx context.Con
|
|||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_resend_otp_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 model.ResendOTPRequest
|
||||
if tmp, ok := rawArgs["params"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||
arg0, err = ec.unmarshalNResendOTPRequest2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResendOTPRequest(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["params"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_resend_verify_email_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
|
@ -2646,6 +2750,21 @@ func (ec *executionContext) field_Mutation_verify_email_args(ctx context.Context
|
|||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_verify_otp_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 model.VerifyOTPRequest
|
||||
if tmp, ok := rawArgs["params"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||
arg0, err = ec.unmarshalNVerifyOTPRequest2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerifyOTPRequest(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["params"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
|
@ -2854,6 +2973,38 @@ func (ec *executionContext) _AuthResponse_message(ctx context.Context, field gra
|
|||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _AuthResponse_should_show_otp_screen(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "AuthResponse",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.ShouldShowOtpScreen, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*bool)
|
||||
fc.Result = res
|
||||
return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _AuthResponse_access_token(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -4339,6 +4490,76 @@ func (ec *executionContext) _Env_DISABLE_STRONG_PASSWORD(ctx context.Context, fi
|
|||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Env_DISABLE_MULTI_FACTOR_AUTHENTICATION(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Env",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.DisableMultiFactorAuthentication, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
fc.Result = res
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Env_ENFORCE_MULTI_FACTOR_AUTHENTICATION(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Env",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.EnforceMultiFactorAuthentication, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
fc.Result = res
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Env_ROLES(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -5437,6 +5658,41 @@ func (ec *executionContext) _Meta_is_strong_password_enabled(ctx context.Context
|
|||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Meta_is_multi_factor_auth_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Meta",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.IsMultiFactorAuthEnabled, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
fc.Result = res
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_signup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -5850,6 +6106,90 @@ func (ec *executionContext) _Mutation_revoke(ctx context.Context, field graphql.
|
|||
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_verify_otp(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Mutation",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
rawArgs := field.ArgumentMap(ec.Variables)
|
||||
args, err := ec.field_Mutation_verify_otp_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Mutation().VerifyOtp(rctx, args["params"].(model.VerifyOTPRequest))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*model.AuthResponse)
|
||||
fc.Result = res
|
||||
return ec.marshalNAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_resend_otp(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Mutation",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
rawArgs := field.ArgumentMap(ec.Variables)
|
||||
args, err := ec.field_Mutation_resend_otp_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Mutation().ResendOtp(rctx, args["params"].(model.ResendOTPRequest))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*model.Response)
|
||||
fc.Result = res
|
||||
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation__delete_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -7934,6 +8274,38 @@ func (ec *executionContext) _User_revoked_timestamp(ctx context.Context, field g
|
|||
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _User_is_multi_factor_auth_enabled(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "User",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.IsMultiFactorAuthEnabled, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*bool)
|
||||
fc.Result = res
|
||||
return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Users_pagination(ctx context.Context, field graphql.CollectedField, obj *model.Users) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -10597,6 +10969,29 @@ func (ec *executionContext) unmarshalInputPaginationInput(ctx context.Context, o
|
|||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputResendOTPRequest(ctx context.Context, obj interface{}) (model.ResendOTPRequest, error) {
|
||||
var it model.ResendOTPRequest
|
||||
asMap := map[string]interface{}{}
|
||||
for k, v := range obj.(map[string]interface{}) {
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
for k, v := range asMap {
|
||||
switch k {
|
||||
case "email":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
it.Email, err = ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputResendVerifyEmailInput(ctx context.Context, obj interface{}) (model.ResendVerifyEmailInput, error) {
|
||||
var it model.ResendVerifyEmailInput
|
||||
asMap := map[string]interface{}{}
|
||||
|
@ -10819,6 +11214,14 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "is_multi_factor_auth_enabled":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("is_multi_factor_auth_enabled"))
|
||||
it.IsMultiFactorAuthEnabled, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11127,6 +11530,22 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "DISABLE_MULTI_FACTOR_AUTHENTICATION":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DISABLE_MULTI_FACTOR_AUTHENTICATION"))
|
||||
it.DisableMultiFactorAuthentication, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "ENFORCE_MULTI_FACTOR_AUTHENTICATION":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("ENFORCE_MULTI_FACTOR_AUTHENTICATION"))
|
||||
it.EnforceMultiFactorAuthentication, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "ROLES":
|
||||
var err error
|
||||
|
||||
|
@ -11366,6 +11785,14 @@ func (ec *executionContext) unmarshalInputUpdateProfileInput(ctx context.Context
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "is_multi_factor_auth_enabled":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("is_multi_factor_auth_enabled"))
|
||||
it.IsMultiFactorAuthEnabled, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11477,6 +11904,14 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "is_multi_factor_auth_enabled":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("is_multi_factor_auth_enabled"))
|
||||
it.IsMultiFactorAuthEnabled, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11600,6 +12035,37 @@ func (ec *executionContext) unmarshalInputVerifyEmailInput(ctx context.Context,
|
|||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputVerifyOTPRequest(ctx context.Context, obj interface{}) (model.VerifyOTPRequest, error) {
|
||||
var it model.VerifyOTPRequest
|
||||
asMap := map[string]interface{}{}
|
||||
for k, v := range obj.(map[string]interface{}) {
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
for k, v := range asMap {
|
||||
switch k {
|
||||
case "email":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
|
||||
it.Email, err = ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "otp":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("otp"))
|
||||
it.Otp, err = ec.unmarshalNString2string(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputWebhookRequest(ctx context.Context, obj interface{}) (model.WebhookRequest, error) {
|
||||
var it model.WebhookRequest
|
||||
asMap := map[string]interface{}{}
|
||||
|
@ -11647,6 +12113,8 @@ func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.Selection
|
|||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "should_show_otp_screen":
|
||||
out.Values[i] = ec._AuthResponse_should_show_otp_screen(ctx, field, obj)
|
||||
case "access_token":
|
||||
out.Values[i] = ec._AuthResponse_access_token(ctx, field, obj)
|
||||
case "id_token":
|
||||
|
@ -11848,6 +12316,16 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
|
|||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "DISABLE_MULTI_FACTOR_AUTHENTICATION":
|
||||
out.Values[i] = ec._Env_DISABLE_MULTI_FACTOR_AUTHENTICATION(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "ENFORCE_MULTI_FACTOR_AUTHENTICATION":
|
||||
out.Values[i] = ec._Env_ENFORCE_MULTI_FACTOR_AUTHENTICATION(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "ROLES":
|
||||
out.Values[i] = ec._Env_ROLES(ctx, field, obj)
|
||||
case "PROTECTED_ROLES":
|
||||
|
@ -12022,6 +12500,11 @@ func (ec *executionContext) _Meta(ctx context.Context, sel ast.SelectionSet, obj
|
|||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "is_multi_factor_auth_enabled":
|
||||
out.Values[i] = ec._Meta_is_multi_factor_auth_enabled(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
|
@ -12098,6 +12581,16 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
|||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "verify_otp":
|
||||
out.Values[i] = ec._Mutation_verify_otp(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "resend_otp":
|
||||
out.Values[i] = ec._Mutation_resend_otp(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "_delete_user":
|
||||
out.Values[i] = ec._Mutation__delete_user(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
|
@ -12549,6 +13042,8 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
|
|||
out.Values[i] = ec._User_updated_at(ctx, field, obj)
|
||||
case "revoked_timestamp":
|
||||
out.Values[i] = ec._User_revoked_timestamp(ctx, field, obj)
|
||||
case "is_multi_factor_auth_enabled":
|
||||
out.Values[i] = ec._User_is_multi_factor_auth_enabled(ctx, field, obj)
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
|
@ -13325,6 +13820,11 @@ func (ec *executionContext) marshalNPagination2ᚖgithubᚗcomᚋauthorizerdev
|
|||
return ec._Pagination(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNResendOTPRequest2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResendOTPRequest(ctx context.Context, v interface{}) (model.ResendOTPRequest, error) {
|
||||
res, err := ec.unmarshalInputResendOTPRequest(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNResendVerifyEmailInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResendVerifyEmailInput(ctx context.Context, v interface{}) (model.ResendVerifyEmailInput, error) {
|
||||
res, err := ec.unmarshalInputResendVerifyEmailInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
|
@ -13618,6 +14118,11 @@ func (ec *executionContext) unmarshalNVerifyEmailInput2githubᚗcomᚋauthorizer
|
|||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNVerifyOTPRequest2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerifyOTPRequest(ctx context.Context, v interface{}) (model.VerifyOTPRequest, error) {
|
||||
res, err := ec.unmarshalInputVerifyOTPRequest(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNWebhook2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐWebhook(ctx context.Context, sel ast.SelectionSet, v model.Webhook) graphql.Marshaler {
|
||||
return ec._Webhook(ctx, sel, &v)
|
||||
}
|
||||
|
|
|
@ -24,12 +24,13 @@ type AdminSignupInput struct {
|
|||
}
|
||||
|
||||
type AuthResponse struct {
|
||||
Message string `json:"message"`
|
||||
AccessToken *string `json:"access_token"`
|
||||
IDToken *string `json:"id_token"`
|
||||
RefreshToken *string `json:"refresh_token"`
|
||||
ExpiresIn *int64 `json:"expires_in"`
|
||||
User *User `json:"user"`
|
||||
Message string `json:"message"`
|
||||
ShouldShowOtpScreen *bool `json:"should_show_otp_screen"`
|
||||
AccessToken *string `json:"access_token"`
|
||||
IDToken *string `json:"id_token"`
|
||||
RefreshToken *string `json:"refresh_token"`
|
||||
ExpiresIn *int64 `json:"expires_in"`
|
||||
User *User `json:"user"`
|
||||
}
|
||||
|
||||
type DeleteEmailTemplateRequest struct {
|
||||
|
@ -55,54 +56,56 @@ type EmailTemplates struct {
|
|||
}
|
||||
|
||||
type Env struct {
|
||||
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
|
||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
||||
DatabaseName *string `json:"DATABASE_NAME"`
|
||||
DatabaseURL *string `json:"DATABASE_URL"`
|
||||
DatabaseType *string `json:"DATABASE_TYPE"`
|
||||
DatabaseUsername *string `json:"DATABASE_USERNAME"`
|
||||
DatabasePassword *string `json:"DATABASE_PASSWORD"`
|
||||
DatabaseHost *string `json:"DATABASE_HOST"`
|
||||
DatabasePort *string `json:"DATABASE_PORT"`
|
||||
ClientID string `json:"CLIENT_ID"`
|
||||
ClientSecret string `json:"CLIENT_SECRET"`
|
||||
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
||||
SMTPHost *string `json:"SMTP_HOST"`
|
||||
SMTPPort *string `json:"SMTP_PORT"`
|
||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
||||
JwtType *string `json:"JWT_TYPE"`
|
||||
JwtSecret *string `json:"JWT_SECRET"`
|
||||
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY"`
|
||||
JwtPublicKey *string `json:"JWT_PUBLIC_KEY"`
|
||||
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
||||
AppURL *string `json:"APP_URL"`
|
||||
RedisURL *string `json:"REDIS_URL"`
|
||||
ResetPasswordURL *string `json:"RESET_PASSWORD_URL"`
|
||||
DisableEmailVerification bool `json:"DISABLE_EMAIL_VERIFICATION"`
|
||||
DisableBasicAuthentication bool `json:"DISABLE_BASIC_AUTHENTICATION"`
|
||||
DisableMagicLinkLogin bool `json:"DISABLE_MAGIC_LINK_LOGIN"`
|
||||
DisableLoginPage bool `json:"DISABLE_LOGIN_PAGE"`
|
||||
DisableSignUp bool `json:"DISABLE_SIGN_UP"`
|
||||
DisableRedisForEnv bool `json:"DISABLE_REDIS_FOR_ENV"`
|
||||
DisableStrongPassword bool `json:"DISABLE_STRONG_PASSWORD"`
|
||||
Roles []string `json:"ROLES"`
|
||||
ProtectedRoles []string `json:"PROTECTED_ROLES"`
|
||||
DefaultRoles []string `json:"DEFAULT_ROLES"`
|
||||
JwtRoleClaim *string `json:"JWT_ROLE_CLAIM"`
|
||||
GoogleClientID *string `json:"GOOGLE_CLIENT_ID"`
|
||||
GoogleClientSecret *string `json:"GOOGLE_CLIENT_SECRET"`
|
||||
GithubClientID *string `json:"GITHUB_CLIENT_ID"`
|
||||
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"`
|
||||
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"`
|
||||
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"`
|
||||
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID"`
|
||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
||||
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
||||
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
||||
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
||||
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
||||
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
|
||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
||||
DatabaseName *string `json:"DATABASE_NAME"`
|
||||
DatabaseURL *string `json:"DATABASE_URL"`
|
||||
DatabaseType *string `json:"DATABASE_TYPE"`
|
||||
DatabaseUsername *string `json:"DATABASE_USERNAME"`
|
||||
DatabasePassword *string `json:"DATABASE_PASSWORD"`
|
||||
DatabaseHost *string `json:"DATABASE_HOST"`
|
||||
DatabasePort *string `json:"DATABASE_PORT"`
|
||||
ClientID string `json:"CLIENT_ID"`
|
||||
ClientSecret string `json:"CLIENT_SECRET"`
|
||||
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
||||
SMTPHost *string `json:"SMTP_HOST"`
|
||||
SMTPPort *string `json:"SMTP_PORT"`
|
||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
||||
JwtType *string `json:"JWT_TYPE"`
|
||||
JwtSecret *string `json:"JWT_SECRET"`
|
||||
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY"`
|
||||
JwtPublicKey *string `json:"JWT_PUBLIC_KEY"`
|
||||
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
||||
AppURL *string `json:"APP_URL"`
|
||||
RedisURL *string `json:"REDIS_URL"`
|
||||
ResetPasswordURL *string `json:"RESET_PASSWORD_URL"`
|
||||
DisableEmailVerification bool `json:"DISABLE_EMAIL_VERIFICATION"`
|
||||
DisableBasicAuthentication bool `json:"DISABLE_BASIC_AUTHENTICATION"`
|
||||
DisableMagicLinkLogin bool `json:"DISABLE_MAGIC_LINK_LOGIN"`
|
||||
DisableLoginPage bool `json:"DISABLE_LOGIN_PAGE"`
|
||||
DisableSignUp bool `json:"DISABLE_SIGN_UP"`
|
||||
DisableRedisForEnv bool `json:"DISABLE_REDIS_FOR_ENV"`
|
||||
DisableStrongPassword bool `json:"DISABLE_STRONG_PASSWORD"`
|
||||
DisableMultiFactorAuthentication bool `json:"DISABLE_MULTI_FACTOR_AUTHENTICATION"`
|
||||
EnforceMultiFactorAuthentication bool `json:"ENFORCE_MULTI_FACTOR_AUTHENTICATION"`
|
||||
Roles []string `json:"ROLES"`
|
||||
ProtectedRoles []string `json:"PROTECTED_ROLES"`
|
||||
DefaultRoles []string `json:"DEFAULT_ROLES"`
|
||||
JwtRoleClaim *string `json:"JWT_ROLE_CLAIM"`
|
||||
GoogleClientID *string `json:"GOOGLE_CLIENT_ID"`
|
||||
GoogleClientSecret *string `json:"GOOGLE_CLIENT_SECRET"`
|
||||
GithubClientID *string `json:"GITHUB_CLIENT_ID"`
|
||||
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"`
|
||||
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"`
|
||||
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"`
|
||||
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID"`
|
||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
||||
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
||||
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
||||
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
||||
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
|
@ -164,6 +167,7 @@ type Meta struct {
|
|||
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
||||
IsSignUpEnabled bool `json:"is_sign_up_enabled"`
|
||||
IsStrongPasswordEnabled bool `json:"is_strong_password_enabled"`
|
||||
IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"`
|
||||
}
|
||||
|
||||
type OAuthRevokeInput struct {
|
||||
|
@ -186,6 +190,10 @@ type PaginationInput struct {
|
|||
Page *int64 `json:"page"`
|
||||
}
|
||||
|
||||
type ResendOTPRequest struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type ResendVerifyEmailInput struct {
|
||||
Email string `json:"email"`
|
||||
Identifier string `json:"identifier"`
|
||||
|
@ -207,20 +215,21 @@ type SessionQueryInput struct {
|
|||
}
|
||||
|
||||
type SignUpInput struct {
|
||||
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:"confirm_password"`
|
||||
Roles []string `json:"roles"`
|
||||
Scope []string `json:"scope"`
|
||||
RedirectURI *string `json:"redirect_uri"`
|
||||
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:"confirm_password"`
|
||||
Roles []string `json:"roles"`
|
||||
Scope []string `json:"scope"`
|
||||
RedirectURI *string `json:"redirect_uri"`
|
||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
||||
}
|
||||
|
||||
type TestEndpointRequest struct {
|
||||
|
@ -246,75 +255,79 @@ type UpdateEmailTemplateRequest struct {
|
|||
}
|
||||
|
||||
type UpdateEnvInput struct {
|
||||
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
|
||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
||||
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
||||
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
|
||||
SMTPHost *string `json:"SMTP_HOST"`
|
||||
SMTPPort *string `json:"SMTP_PORT"`
|
||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
||||
JwtType *string `json:"JWT_TYPE"`
|
||||
JwtSecret *string `json:"JWT_SECRET"`
|
||||
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY"`
|
||||
JwtPublicKey *string `json:"JWT_PUBLIC_KEY"`
|
||||
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
||||
AppURL *string `json:"APP_URL"`
|
||||
ResetPasswordURL *string `json:"RESET_PASSWORD_URL"`
|
||||
DisableEmailVerification *bool `json:"DISABLE_EMAIL_VERIFICATION"`
|
||||
DisableBasicAuthentication *bool `json:"DISABLE_BASIC_AUTHENTICATION"`
|
||||
DisableMagicLinkLogin *bool `json:"DISABLE_MAGIC_LINK_LOGIN"`
|
||||
DisableLoginPage *bool `json:"DISABLE_LOGIN_PAGE"`
|
||||
DisableSignUp *bool `json:"DISABLE_SIGN_UP"`
|
||||
DisableRedisForEnv *bool `json:"DISABLE_REDIS_FOR_ENV"`
|
||||
DisableStrongPassword *bool `json:"DISABLE_STRONG_PASSWORD"`
|
||||
Roles []string `json:"ROLES"`
|
||||
ProtectedRoles []string `json:"PROTECTED_ROLES"`
|
||||
DefaultRoles []string `json:"DEFAULT_ROLES"`
|
||||
JwtRoleClaim *string `json:"JWT_ROLE_CLAIM"`
|
||||
GoogleClientID *string `json:"GOOGLE_CLIENT_ID"`
|
||||
GoogleClientSecret *string `json:"GOOGLE_CLIENT_SECRET"`
|
||||
GithubClientID *string `json:"GITHUB_CLIENT_ID"`
|
||||
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"`
|
||||
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"`
|
||||
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"`
|
||||
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID"`
|
||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
||||
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
||||
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
||||
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
||||
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
||||
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
|
||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
||||
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
||||
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
|
||||
SMTPHost *string `json:"SMTP_HOST"`
|
||||
SMTPPort *string `json:"SMTP_PORT"`
|
||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
||||
JwtType *string `json:"JWT_TYPE"`
|
||||
JwtSecret *string `json:"JWT_SECRET"`
|
||||
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY"`
|
||||
JwtPublicKey *string `json:"JWT_PUBLIC_KEY"`
|
||||
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
||||
AppURL *string `json:"APP_URL"`
|
||||
ResetPasswordURL *string `json:"RESET_PASSWORD_URL"`
|
||||
DisableEmailVerification *bool `json:"DISABLE_EMAIL_VERIFICATION"`
|
||||
DisableBasicAuthentication *bool `json:"DISABLE_BASIC_AUTHENTICATION"`
|
||||
DisableMagicLinkLogin *bool `json:"DISABLE_MAGIC_LINK_LOGIN"`
|
||||
DisableLoginPage *bool `json:"DISABLE_LOGIN_PAGE"`
|
||||
DisableSignUp *bool `json:"DISABLE_SIGN_UP"`
|
||||
DisableRedisForEnv *bool `json:"DISABLE_REDIS_FOR_ENV"`
|
||||
DisableStrongPassword *bool `json:"DISABLE_STRONG_PASSWORD"`
|
||||
DisableMultiFactorAuthentication *bool `json:"DISABLE_MULTI_FACTOR_AUTHENTICATION"`
|
||||
EnforceMultiFactorAuthentication *bool `json:"ENFORCE_MULTI_FACTOR_AUTHENTICATION"`
|
||||
Roles []string `json:"ROLES"`
|
||||
ProtectedRoles []string `json:"PROTECTED_ROLES"`
|
||||
DefaultRoles []string `json:"DEFAULT_ROLES"`
|
||||
JwtRoleClaim *string `json:"JWT_ROLE_CLAIM"`
|
||||
GoogleClientID *string `json:"GOOGLE_CLIENT_ID"`
|
||||
GoogleClientSecret *string `json:"GOOGLE_CLIENT_SECRET"`
|
||||
GithubClientID *string `json:"GITHUB_CLIENT_ID"`
|
||||
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"`
|
||||
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"`
|
||||
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"`
|
||||
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID"`
|
||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
||||
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
||||
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
||||
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
||||
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
||||
}
|
||||
|
||||
type UpdateProfileInput struct {
|
||||
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"`
|
||||
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"`
|
||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
||||
}
|
||||
|
||||
type UpdateUserInput struct {
|
||||
ID string `json:"id"`
|
||||
Email *string `json:"email"`
|
||||
EmailVerified *bool `json:"email_verified"`
|
||||
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"`
|
||||
ID string `json:"id"`
|
||||
Email *string `json:"email"`
|
||||
EmailVerified *bool `json:"email_verified"`
|
||||
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"`
|
||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
||||
}
|
||||
|
||||
type UpdateWebhookRequest struct {
|
||||
|
@ -326,24 +339,25 @@ type UpdateWebhookRequest struct {
|
|||
}
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
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"`
|
||||
RevokedTimestamp *int64 `json:"revoked_timestamp"`
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
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"`
|
||||
RevokedTimestamp *int64 `json:"revoked_timestamp"`
|
||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
||||
}
|
||||
|
||||
type Users struct {
|
||||
|
@ -382,6 +396,11 @@ type VerifyEmailInput struct {
|
|||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type VerifyOTPRequest struct {
|
||||
Email string `json:"email"`
|
||||
Otp string `json:"otp"`
|
||||
}
|
||||
|
||||
type Webhook struct {
|
||||
ID string `json:"id"`
|
||||
EventName *string `json:"event_name"`
|
||||
|
|
|
@ -25,6 +25,7 @@ type Meta {
|
|||
is_magic_link_login_enabled: Boolean!
|
||||
is_sign_up_enabled: Boolean!
|
||||
is_strong_password_enabled: Boolean!
|
||||
is_multi_factor_auth_enabled: Boolean!
|
||||
}
|
||||
|
||||
type User {
|
||||
|
@ -47,6 +48,7 @@ type User {
|
|||
created_at: Int64
|
||||
updated_at: Int64
|
||||
revoked_timestamp: Int64
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
}
|
||||
|
||||
type Users {
|
||||
|
@ -78,6 +80,7 @@ type Error {
|
|||
|
||||
type AuthResponse {
|
||||
message: String!
|
||||
should_show_otp_screen: Boolean
|
||||
access_token: String
|
||||
id_token: String
|
||||
refresh_token: String
|
||||
|
@ -122,6 +125,8 @@ type Env {
|
|||
DISABLE_SIGN_UP: Boolean!
|
||||
DISABLE_REDIS_FOR_ENV: Boolean!
|
||||
DISABLE_STRONG_PASSWORD: Boolean!
|
||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean!
|
||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean!
|
||||
ROLES: [String!]
|
||||
PROTECTED_ROLES: [String!]
|
||||
DEFAULT_ROLES: [String!]
|
||||
|
@ -223,6 +228,8 @@ input UpdateEnvInput {
|
|||
DISABLE_SIGN_UP: Boolean
|
||||
DISABLE_REDIS_FOR_ENV: Boolean
|
||||
DISABLE_STRONG_PASSWORD: Boolean
|
||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean
|
||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean
|
||||
ROLES: [String!]
|
||||
PROTECTED_ROLES: [String!]
|
||||
DEFAULT_ROLES: [String!]
|
||||
|
@ -264,6 +271,7 @@ input SignUpInput {
|
|||
roles: [String!]
|
||||
scope: [String!]
|
||||
redirect_uri: String
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
|
@ -295,6 +303,7 @@ input UpdateProfileInput {
|
|||
birthdate: String
|
||||
phone_number: String
|
||||
picture: String
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
}
|
||||
|
||||
input UpdateUserInput {
|
||||
|
@ -310,6 +319,7 @@ input UpdateUserInput {
|
|||
phone_number: String
|
||||
picture: String
|
||||
roles: [String]
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
}
|
||||
|
||||
input ForgotPasswordInput {
|
||||
|
@ -420,6 +430,15 @@ input DeleteEmailTemplateRequest {
|
|||
id: ID!
|
||||
}
|
||||
|
||||
input VerifyOTPRequest {
|
||||
email: String!
|
||||
otp: String!
|
||||
}
|
||||
|
||||
input ResendOTPRequest {
|
||||
email: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
signup(params: SignUpInput!): AuthResponse!
|
||||
login(params: LoginInput!): AuthResponse!
|
||||
|
@ -431,6 +450,8 @@ type Mutation {
|
|||
forgot_password(params: ForgotPasswordInput!): Response!
|
||||
reset_password(params: ResetPasswordInput!): Response!
|
||||
revoke(params: OAuthRevokeInput!): Response!
|
||||
verify_otp(params: VerifyOTPRequest!): AuthResponse!
|
||||
resend_otp(params: ResendOTPRequest!): Response!
|
||||
# admin only apis
|
||||
_delete_user(params: DeleteUserInput!): Response!
|
||||
_update_user(params: UpdateUserInput!): User!
|
||||
|
|
|
@ -51,6 +51,14 @@ func (r *mutationResolver) Revoke(ctx context.Context, params model.OAuthRevokeI
|
|||
return resolvers.RevokeResolver(ctx, params)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) VerifyOtp(ctx context.Context, params model.VerifyOTPRequest) (*model.AuthResponse, error) {
|
||||
return resolvers.VerifyOtpResolver(ctx, params)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) ResendOtp(ctx context.Context, params model.ResendOTPRequest) (*model.Response, error) {
|
||||
return resolvers.ResendOTPResolver(ctx, params)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
||||
return resolvers.DeleteUserResolver(ctx, params)
|
||||
}
|
||||
|
|
|
@ -25,12 +25,15 @@ func InitMemStore() error {
|
|||
constants.EnvKeyOrganizationLogo: "https://www.authorizer.dev/images/logo.png",
|
||||
|
||||
// boolean envs
|
||||
constants.EnvKeyDisableBasicAuthentication: false,
|
||||
constants.EnvKeyDisableMagicLinkLogin: false,
|
||||
constants.EnvKeyDisableEmailVerification: false,
|
||||
constants.EnvKeyDisableLoginPage: false,
|
||||
constants.EnvKeyDisableSignUp: false,
|
||||
constants.EnvKeyDisableStrongPassword: false,
|
||||
constants.EnvKeyDisableBasicAuthentication: false,
|
||||
constants.EnvKeyDisableMagicLinkLogin: false,
|
||||
constants.EnvKeyDisableEmailVerification: false,
|
||||
constants.EnvKeyDisableLoginPage: false,
|
||||
constants.EnvKeyDisableSignUp: false,
|
||||
constants.EnvKeyDisableStrongPassword: false,
|
||||
constants.EnvKeyIsEmailServiceEnabled: false,
|
||||
constants.EnvKeyEnforceMultiFactorAuthentication: false,
|
||||
constants.EnvKeyDisableMultiFactorAuthentication: false,
|
||||
}
|
||||
|
||||
requiredEnvs := RequiredEnvStoreObj.GetRequiredEnv()
|
||||
|
|
|
@ -39,6 +39,7 @@ func (s *SessionStore) Set(key string, subKey, value string) {
|
|||
func (s *SessionStore) RemoveAll(key string) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
delete(s.store, key)
|
||||
}
|
||||
|
||||
|
@ -53,6 +54,9 @@ func (s *SessionStore) Remove(key, subKey string) {
|
|||
|
||||
// Get all the values for given key
|
||||
func (s *SessionStore) GetAll(key string) map[string]string {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if _, ok := s.store[key]; !ok {
|
||||
s.store[key] = make(map[string]string)
|
||||
}
|
||||
|
@ -63,6 +67,7 @@ func (s *SessionStore) GetAll(key string) map[string]string {
|
|||
func (s *SessionStore) RemoveByNamespace(namespace string) error {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
for key := range s.store {
|
||||
if strings.Contains(key, namespace+":") {
|
||||
delete(s.store, key)
|
||||
|
|
|
@ -160,7 +160,7 @@ func (c *provider) GetEnvStore() (map[string]interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
for key, value := range data {
|
||||
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword {
|
||||
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication {
|
||||
boolValue, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
|
|
@ -170,6 +170,8 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
|
|||
res.DisableLoginPage = store[constants.EnvKeyDisableLoginPage].(bool)
|
||||
res.DisableSignUp = store[constants.EnvKeyDisableSignUp].(bool)
|
||||
res.DisableStrongPassword = store[constants.EnvKeyDisableStrongPassword].(bool)
|
||||
res.EnforceMultiFactorAuthentication = store[constants.EnvKeyEnforceMultiFactorAuthentication].(bool)
|
||||
res.DisableMultiFactorAuthentication = store[constants.EnvKeyDisableMultiFactorAuthentication].(bool)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/validators"
|
||||
|
@ -35,13 +36,13 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
|
|||
}
|
||||
|
||||
// this feature is only allowed if email server is configured
|
||||
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
|
||||
EnvKeyIsEmailServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
|
||||
if err != nil {
|
||||
log.Debug("Error getting email verification disabled: ", err)
|
||||
isEmailVerificationDisabled = true
|
||||
EnvKeyIsEmailServiceEnabled = false
|
||||
}
|
||||
|
||||
if isEmailVerificationDisabled {
|
||||
if !EnvKeyIsEmailServiceEnabled {
|
||||
log.Debug("Email server is not configured")
|
||||
return nil, errors.New("email sending is disabled")
|
||||
}
|
||||
|
@ -136,6 +137,15 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
|
|||
user.SignupMethods = constants.AuthRecipeMethodBasicAuth
|
||||
verificationRequest.Identifier = constants.VerificationTypeForgotPassword
|
||||
|
||||
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)
|
||||
if err != nil {
|
||||
log.Debug("MFA service not enabled: ", err)
|
||||
isMFAEnforced = false
|
||||
}
|
||||
|
||||
if isMFAEnforced {
|
||||
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true)
|
||||
}
|
||||
verifyEmailURL = appURL + "/setup-password"
|
||||
|
||||
}
|
||||
|
|
|
@ -13,8 +13,10 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/email"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/validators"
|
||||
|
@ -97,6 +99,42 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||
scope = params.Scope
|
||||
}
|
||||
|
||||
isEmailServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
|
||||
if err != nil || !isEmailServiceEnabled {
|
||||
log.Debug("Email service not enabled: ", err)
|
||||
}
|
||||
|
||||
isMFADisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication)
|
||||
if err != nil || !isEmailServiceEnabled {
|
||||
log.Debug("MFA service not enabled: ", err)
|
||||
}
|
||||
|
||||
// If email service is not enabled continue the process in any way
|
||||
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && isEmailServiceEnabled && !isMFADisabled {
|
||||
otp := utils.GenerateOTP()
|
||||
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
|
||||
Email: user.Email,
|
||||
Otp: otp,
|
||||
ExpiresAt: time.Now().Add(1 * time.Minute).Unix(),
|
||||
})
|
||||
if err != nil {
|
||||
log.Debug("Failed to add otp: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := email.SendOtpMail(user.Email, otpData.Otp)
|
||||
if err != nil {
|
||||
log.Debug("Failed to send otp email: ", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return &model.AuthResponse{
|
||||
Message: "Please check the OTP in your inbox",
|
||||
ShouldShowOtpScreen: refs.NewBoolRef(true),
|
||||
}, nil
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth)
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token", err)
|
||||
|
|
|
@ -107,6 +107,12 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
|
|||
isSignUpDisabled = true
|
||||
}
|
||||
|
||||
isMultiFactorAuthenticationEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get Disable Multi Factor Authentication from environment variable", err)
|
||||
isSignUpDisabled = true
|
||||
}
|
||||
|
||||
metaInfo := model.Meta{
|
||||
Version: constants.VERSION,
|
||||
ClientID: clientID,
|
||||
|
@ -120,6 +126,7 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
|
|||
IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled,
|
||||
IsSignUpEnabled: !isSignUpDisabled,
|
||||
IsStrongPasswordEnabled: !isStrongPasswordDisabled,
|
||||
IsMultiFactorAuthEnabled: !isMultiFactorAuthenticationEnabled,
|
||||
}
|
||||
return &metaInfo, nil
|
||||
}
|
||||
|
|
96
server/resolvers/resend_otp.go
Normal file
96
server/resolvers/resend_otp.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/email"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
// ResendOTPResolver is a resolver for resend otp mutation
|
||||
func ResendOTPResolver(ctx context.Context, params model.ResendOTPRequest) (*model.Response, error) {
|
||||
log := log.WithFields(log.Fields{
|
||||
"email": params.Email,
|
||||
})
|
||||
params.Email = strings.ToLower(params.Email)
|
||||
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get user by email: ", err)
|
||||
return nil, fmt.Errorf(`user with this email not found`)
|
||||
}
|
||||
|
||||
if user.RevokedTimestamp != nil {
|
||||
log.Debug("User access is revoked")
|
||||
return nil, fmt.Errorf(`user access has been revoked`)
|
||||
}
|
||||
|
||||
if user.EmailVerifiedAt == nil {
|
||||
log.Debug("User email is not verified")
|
||||
return nil, fmt.Errorf(`email not verified`)
|
||||
}
|
||||
|
||||
if !refs.BoolValue(user.IsMultiFactorAuthEnabled) {
|
||||
log.Debug("User multi factor authentication is not enabled")
|
||||
return nil, fmt.Errorf(`multi factor authentication not enabled`)
|
||||
}
|
||||
|
||||
isEmailServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
|
||||
if err != nil || !isEmailServiceEnabled {
|
||||
log.Debug("Email service not enabled: ", err)
|
||||
return nil, errors.New("email service not enabled")
|
||||
}
|
||||
|
||||
isMFADisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication)
|
||||
if err != nil || isMFADisabled {
|
||||
log.Debug("MFA service not enabled: ", err)
|
||||
return nil, errors.New("multi factor authentication is disabled for this instance")
|
||||
}
|
||||
|
||||
// get otp by email
|
||||
otpData, err := db.Provider.GetOTPByEmail(ctx, params.Email)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get otp for given email: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if otpData == nil {
|
||||
log.Debug("No otp found for given email: ", params.Email)
|
||||
return &model.Response{
|
||||
Message: "Failed to get for given email",
|
||||
}, errors.New("failed to get otp for given email")
|
||||
}
|
||||
|
||||
otp := utils.GenerateOTP()
|
||||
otpData, err = db.Provider.UpsertOTP(ctx, &models.OTP{
|
||||
Email: user.Email,
|
||||
Otp: otp,
|
||||
ExpiresAt: time.Now().Add(1 * time.Minute).Unix(),
|
||||
})
|
||||
if err != nil {
|
||||
log.Debug("Error generating new otp: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := email.SendOtpMail(params.Email, otp)
|
||||
if err != nil {
|
||||
log.Debug("Error sending otp email: ", otp)
|
||||
}
|
||||
}()
|
||||
|
||||
return &model.Response{
|
||||
Message: `OTP has been sent. Please check your inbox`,
|
||||
}, nil
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/validators"
|
||||
|
@ -84,6 +85,16 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
|
|||
signupMethod := user.SignupMethods
|
||||
if !strings.Contains(signupMethod, constants.AuthRecipeMethodBasicAuth) {
|
||||
signupMethod = signupMethod + "," + constants.AuthRecipeMethodBasicAuth
|
||||
|
||||
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)
|
||||
if err != nil {
|
||||
log.Debug("MFA service not enabled: ", err)
|
||||
isMFAEnforced = false
|
||||
}
|
||||
|
||||
if isMFAEnforced {
|
||||
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true)
|
||||
}
|
||||
}
|
||||
user.SignupMethods = signupMethod
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/validators"
|
||||
|
@ -157,6 +158,20 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||
user.Picture = params.Picture
|
||||
}
|
||||
|
||||
if params.IsMultiFactorAuthEnabled != nil {
|
||||
user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled
|
||||
}
|
||||
|
||||
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)
|
||||
if err != nil {
|
||||
log.Debug("MFA service not enabled: ", err)
|
||||
isMFAEnforced = false
|
||||
}
|
||||
|
||||
if isMFAEnforced {
|
||||
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true)
|
||||
}
|
||||
|
||||
user.SignupMethods = constants.AuthRecipeMethodBasicAuth
|
||||
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
|
||||
if err != nil {
|
||||
|
|
|
@ -234,6 +234,8 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
|
|||
// handle derivative cases like disabling email verification & magic login
|
||||
// in case SMTP is off but env is set to true
|
||||
if updatedData[constants.EnvKeySmtpHost] == "" || updatedData[constants.EnvKeySmtpUsername] == "" || updatedData[constants.EnvKeySmtpPassword] == "" || updatedData[constants.EnvKeySenderEmail] == "" && updatedData[constants.EnvKeySmtpPort] == "" {
|
||||
updatedData[constants.EnvKeyIsEmailServiceEnabled] = false
|
||||
updatedData[constants.EnvKeyDisableMultiFactorAuthentication] = true
|
||||
if !updatedData[constants.EnvKeyDisableEmailVerification].(bool) {
|
||||
updatedData[constants.EnvKeyDisableEmailVerification] = true
|
||||
}
|
||||
|
@ -243,6 +245,16 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
|
|||
}
|
||||
}
|
||||
|
||||
if updatedData[constants.EnvKeySmtpHost] != "" || updatedData[constants.EnvKeySmtpUsername] != "" || updatedData[constants.EnvKeySmtpPassword] != "" || updatedData[constants.EnvKeySenderEmail] != "" && updatedData[constants.EnvKeySmtpPort] != "" {
|
||||
updatedData[constants.EnvKeyIsEmailServiceEnabled] = true
|
||||
}
|
||||
|
||||
if !currentData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && updatedData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && !updatedData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) {
|
||||
go db.Provider.UpdateUsers(ctx, map[string]interface{}{
|
||||
"is_multi_factor_auth_enabled": true,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// check the roles change
|
||||
if len(params.Roles) > 0 {
|
||||
if len(params.DefaultRoles) > 0 {
|
||||
|
@ -265,8 +277,6 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
|
|||
}
|
||||
}
|
||||
|
||||
go clearSessionIfRequired(currentData, updatedData)
|
||||
|
||||
// Update local store
|
||||
memorystore.Provider.UpdateEnvStore(updatedData)
|
||||
jwk, err := crypto.GenerateJWKBasedOnEnv()
|
||||
|
@ -320,6 +330,8 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
|
|||
return res, err
|
||||
}
|
||||
|
||||
go clearSessionIfRequired(currentData, updatedData)
|
||||
|
||||
res = &model.Response{
|
||||
Message: "configurations updated successfully",
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -46,7 +47,7 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
|||
}
|
||||
|
||||
// validate if all params are not empty
|
||||
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 && params.NewPassword == nil && params.ConfirmNewPassword == 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 && params.NewPassword == nil && params.ConfirmNewPassword == nil && params.IsMultiFactorAuthEnabled == nil {
|
||||
log.Debug("All params are empty")
|
||||
return res, fmt.Errorf("please enter at least one param to update")
|
||||
}
|
||||
|
@ -94,6 +95,29 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
|||
user.Picture = params.Picture
|
||||
}
|
||||
|
||||
if params.IsMultiFactorAuthEnabled != nil && refs.BoolValue(user.IsMultiFactorAuthEnabled) != refs.BoolValue(params.IsMultiFactorAuthEnabled) {
|
||||
if refs.BoolValue(params.IsMultiFactorAuthEnabled) {
|
||||
isEnvServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
|
||||
if err != nil || !isEnvServiceEnabled {
|
||||
log.Debug("Email service not enabled:")
|
||||
return nil, errors.New("email service not enabled, so cannot enable multi factor authentication")
|
||||
}
|
||||
}
|
||||
|
||||
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)
|
||||
if err != nil {
|
||||
log.Debug("MFA service not enabled: ", err)
|
||||
isMFAEnforced = false
|
||||
}
|
||||
|
||||
if isMFAEnforced && !refs.BoolValue(params.IsMultiFactorAuthEnabled) {
|
||||
log.Debug("Cannot disable mfa service as it is enforced:")
|
||||
return nil, errors.New("cannot disable multi factor authentication as it is enforced by organization")
|
||||
}
|
||||
|
||||
user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled
|
||||
}
|
||||
|
||||
isPasswordChanging := false
|
||||
if params.NewPassword != nil && params.ConfirmNewPassword == nil {
|
||||
isPasswordChanging = true
|
||||
|
|
|
@ -2,6 +2,7 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/validators"
|
||||
|
@ -45,7 +47,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
|||
"user_id": params.ID,
|
||||
})
|
||||
|
||||
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 {
|
||||
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 && params.IsMultiFactorAuthEnabled == nil {
|
||||
log.Debug("No params to update")
|
||||
return res, fmt.Errorf("please enter atleast one param to update")
|
||||
}
|
||||
|
@ -56,38 +58,49 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
|||
return res, fmt.Errorf(`User not found`)
|
||||
}
|
||||
|
||||
if params.GivenName != nil && user.GivenName != params.GivenName {
|
||||
if params.GivenName != nil && refs.StringValue(user.GivenName) != refs.StringValue(params.GivenName) {
|
||||
user.GivenName = params.GivenName
|
||||
}
|
||||
|
||||
if params.FamilyName != nil && user.FamilyName != params.FamilyName {
|
||||
if params.FamilyName != nil && refs.StringValue(user.FamilyName) != refs.StringValue(params.FamilyName) {
|
||||
user.FamilyName = params.FamilyName
|
||||
}
|
||||
|
||||
if params.MiddleName != nil && user.MiddleName != params.MiddleName {
|
||||
if params.MiddleName != nil && refs.StringValue(user.MiddleName) != refs.StringValue(params.MiddleName) {
|
||||
user.MiddleName = params.MiddleName
|
||||
}
|
||||
|
||||
if params.Nickname != nil && user.Nickname != params.Nickname {
|
||||
if params.Nickname != nil && refs.StringValue(user.Nickname) != refs.StringValue(params.Nickname) {
|
||||
user.Nickname = params.Nickname
|
||||
}
|
||||
|
||||
if params.Birthdate != nil && user.Birthdate != params.Birthdate {
|
||||
if params.Birthdate != nil && refs.StringValue(user.Birthdate) != refs.StringValue(params.Birthdate) {
|
||||
user.Birthdate = params.Birthdate
|
||||
}
|
||||
|
||||
if params.Gender != nil && user.Gender != params.Gender {
|
||||
if params.Gender != nil && refs.StringValue(user.Gender) != refs.StringValue(params.Gender) {
|
||||
user.Gender = params.Gender
|
||||
}
|
||||
|
||||
if params.PhoneNumber != nil && user.PhoneNumber != params.PhoneNumber {
|
||||
if params.PhoneNumber != nil && refs.StringValue(user.PhoneNumber) != refs.StringValue(params.PhoneNumber) {
|
||||
user.PhoneNumber = params.PhoneNumber
|
||||
}
|
||||
|
||||
if params.Picture != nil && user.Picture != params.Picture {
|
||||
if params.Picture != nil && refs.StringValue(user.Picture) != refs.StringValue(params.Picture) {
|
||||
user.Picture = params.Picture
|
||||
}
|
||||
|
||||
if params.IsMultiFactorAuthEnabled != nil && refs.BoolValue(user.IsMultiFactorAuthEnabled) != refs.BoolValue(params.IsMultiFactorAuthEnabled) {
|
||||
user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled
|
||||
if refs.BoolValue(params.IsMultiFactorAuthEnabled) {
|
||||
isEnvServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
|
||||
if err != nil || !isEnvServiceEnabled {
|
||||
log.Debug("Email service not enabled:")
|
||||
return nil, errors.New("email service not enabled, so cannot enable multi factor authentication")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if params.EmailVerified != nil {
|
||||
if *params.EmailVerified {
|
||||
now := time.Now().Unix()
|
||||
|
|
104
server/resolvers/verify_otp.go
Normal file
104
server/resolvers/verify_otp.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// VerifyOtpResolver resolver for verify otp mutation
|
||||
func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*model.AuthResponse, error) {
|
||||
var res *model.AuthResponse
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get GinContext: ", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
otp, err := db.Provider.GetOTPByEmail(ctx, params.Email)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get otp request by email: ", err)
|
||||
return res, fmt.Errorf(`invalid email: %s`, err.Error())
|
||||
}
|
||||
|
||||
if params.Otp != otp.Otp {
|
||||
log.Debug("Failed to verify otp request: Incorrect value")
|
||||
return res, fmt.Errorf(`invalid otp`)
|
||||
}
|
||||
|
||||
expiresIn := otp.ExpiresAt - time.Now().Unix()
|
||||
|
||||
if expiresIn < 0 {
|
||||
log.Debug("Failed to verify otp request: Timeout")
|
||||
return res, fmt.Errorf("otp expired")
|
||||
}
|
||||
|
||||
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get user by email: ", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
isSignUp := user.EmailVerifiedAt == nil
|
||||
|
||||
// TODO - Add Login method in DB when we introduce OTP for social media login
|
||||
loginMethod := constants.AuthRecipeMethodBasicAuth
|
||||
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod)
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token: ", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
db.Provider.DeleteOTP(gc, otp)
|
||||
if isSignUp {
|
||||
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, loginMethod, user)
|
||||
} else {
|
||||
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, loginMethod, user)
|
||||
}
|
||||
|
||||
db.Provider.AddSession(ctx, models.Session{
|
||||
UserID: user.ID,
|
||||
UserAgent: utils.GetUserAgent(gc.Request),
|
||||
IP: utils.GetIP(gc.Request),
|
||||
})
|
||||
}()
|
||||
|
||||
authTokenExpiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if authTokenExpiresIn <= 0 {
|
||||
authTokenExpiresIn = 1
|
||||
}
|
||||
|
||||
res = &model.AuthResponse{
|
||||
Message: `OTP verified successfully.`,
|
||||
AccessToken: &authToken.AccessToken.Token,
|
||||
IDToken: &authToken.IDToken.Token,
|
||||
ExpiresIn: &authTokenExpiresIn,
|
||||
User: user.AsAPIUser(),
|
||||
}
|
||||
|
||||
sessionKey := loginMethod + ":" + user.ID
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
return res, nil
|
||||
}
|
99
server/test/resend_otp_test.go
Normal file
99
server/test/resend_otp_test.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func resendOTPTest(t *testing.T, s TestSetup) {
|
||||
t.Helper()
|
||||
t.Run(`should resend otp`, func(t *testing.T) {
|
||||
req, ctx := createContext(s)
|
||||
email := "resend_otp." + s.TestInfo.Email
|
||||
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
|
||||
Email: email,
|
||||
Password: s.TestInfo.Password,
|
||||
ConfirmPassword: s.TestInfo.Password,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
// Login should fail as email is not verified
|
||||
loginRes, err := resolvers.LoginResolver(ctx, model.LoginInput{
|
||||
Email: email,
|
||||
Password: s.TestInfo.Password,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, loginRes)
|
||||
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeBasicAuthSignup)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, email, verificationRequest.Email)
|
||||
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||
Token: verificationRequest.Token,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty")
|
||||
|
||||
// Using access token update profile
|
||||
s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken))
|
||||
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
|
||||
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
|
||||
IsMultiFactorAuthEnabled: refs.NewBoolRef(true),
|
||||
})
|
||||
|
||||
// Resend otp should return error as no initial opt is being sent
|
||||
resendOtpRes, err := resolvers.ResendOTPResolver(ctx, model.ResendOTPRequest{
|
||||
Email: email,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, resendOtpRes)
|
||||
|
||||
// Login should not return error but access token should be empty as otp should have been sent
|
||||
loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{
|
||||
Email: email,
|
||||
Password: s.TestInfo.Password,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, loginRes)
|
||||
assert.Nil(t, loginRes.AccessToken)
|
||||
|
||||
// Get otp from db
|
||||
otp, err := db.Provider.GetOTPByEmail(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, otp.Otp)
|
||||
|
||||
// resend otp
|
||||
resendOtpRes, err = resolvers.ResendOTPResolver(ctx, model.ResendOTPRequest{
|
||||
Email: email,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resendOtpRes.Message)
|
||||
|
||||
newOtp, err := db.Provider.GetOTPByEmail(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, newOtp.Otp)
|
||||
assert.NotEqual(t, otp.Otp, newOtp)
|
||||
|
||||
// Should return error for older otp
|
||||
verifyOtpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
|
||||
Email: email,
|
||||
Otp: otp.Otp,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
|
||||
verifyOtpRes, err = resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
|
||||
Email: email,
|
||||
Otp: newOtp.Otp,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, verifyOtpRes.AccessToken, "", "access token should not be empty")
|
||||
cleanData(email)
|
||||
})
|
||||
}
|
|
@ -33,7 +33,7 @@ func TestResolvers(t *testing.T) {
|
|||
if utils.StringSliceContains(testDBs, constants.DbTypeSqlite) && len(testDBs) == 1 {
|
||||
// do nothing
|
||||
} else {
|
||||
t.Log("waiting for docker containers to spun up")
|
||||
t.Log("waiting for docker containers to start...")
|
||||
// wait for docker containers to spun up
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
|
@ -114,7 +114,10 @@ func TestResolvers(t *testing.T) {
|
|||
metaTests(t, s)
|
||||
inviteUserTest(t, s)
|
||||
validateJwtTokenTest(t, s)
|
||||
verifyOTPTest(t, s)
|
||||
resendOTPTest(t, s)
|
||||
|
||||
updateAllUsersTest(t, s)
|
||||
webhookLogsTest(t, s) // get logs after above resolver tests are done
|
||||
deleteWebhookTest(t, s) // delete webhooks (admin resolver)
|
||||
})
|
||||
|
|
|
@ -57,6 +57,11 @@ func cleanData(email string) {
|
|||
err = db.Provider.DeleteVerificationRequest(ctx, verificationRequest)
|
||||
}
|
||||
|
||||
otp, err := db.Provider.GetOTPByEmail(ctx, email)
|
||||
if err == nil {
|
||||
err = db.Provider.DeleteOTP(ctx, otp)
|
||||
}
|
||||
|
||||
dbUser, err := db.Provider.GetUserByEmail(ctx, email)
|
||||
if err == nil {
|
||||
db.Provider.DeleteUser(ctx, dbUser)
|
||||
|
|
67
server/test/update_all_users_tests.go
Normal file
67
server/test/update_all_users_tests.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func updateAllUsersTest(t *testing.T, s TestSetup) {
|
||||
t.Helper()
|
||||
t.Run("Should update all users", func(t *testing.T) {
|
||||
_, ctx := createContext(s)
|
||||
|
||||
users := []models.User{}
|
||||
for i := 0; i < 10; i++ {
|
||||
user := models.User{
|
||||
Email: fmt.Sprintf("update_all_user_%d_%s", i, s.TestInfo.Email),
|
||||
SignupMethods: constants.AuthRecipeMethodBasicAuth,
|
||||
Roles: "user",
|
||||
}
|
||||
users = append(users, user)
|
||||
u, err := db.Provider.AddUser(ctx, user)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, u)
|
||||
}
|
||||
|
||||
err := db.Provider.UpdateUsers(ctx, map[string]interface{}{
|
||||
"is_multi_factor_auth_enabled": true,
|
||||
}, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
listUsers, err := db.Provider.ListUsers(ctx, model.Pagination{
|
||||
Limit: 20,
|
||||
Offset: 0,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
for _, u := range listUsers.Users {
|
||||
assert.True(t, refs.BoolValue(u.IsMultiFactorAuthEnabled))
|
||||
}
|
||||
|
||||
// // update few users
|
||||
updateIds := []string{listUsers.Users[0].ID, listUsers.Users[1].ID}
|
||||
err = db.Provider.UpdateUsers(ctx, map[string]interface{}{
|
||||
"is_multi_factor_auth_enabled": false,
|
||||
}, updateIds)
|
||||
assert.NoError(t, err)
|
||||
|
||||
listUsers, err = db.Provider.ListUsers(ctx, model.Pagination{
|
||||
Limit: 20,
|
||||
Offset: 0,
|
||||
})
|
||||
for _, u := range listUsers.Users {
|
||||
if utils.StringSliceContains(updateIds, u.ID) {
|
||||
assert.False(t, refs.BoolValue(u.IsMultiFactorAuthEnabled))
|
||||
} else {
|
||||
assert.True(t, refs.BoolValue(u.IsMultiFactorAuthEnabled))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
75
server/test/verify_otp_test.go
Normal file
75
server/test/verify_otp_test.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func verifyOTPTest(t *testing.T, s TestSetup) {
|
||||
t.Helper()
|
||||
t.Run(`should verify otp`, func(t *testing.T) {
|
||||
req, ctx := createContext(s)
|
||||
email := "verify_otp." + s.TestInfo.Email
|
||||
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
|
||||
Email: email,
|
||||
Password: s.TestInfo.Password,
|
||||
ConfirmPassword: s.TestInfo.Password,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
// Login should fail as email is not verified
|
||||
loginRes, err := resolvers.LoginResolver(ctx, model.LoginInput{
|
||||
Email: email,
|
||||
Password: s.TestInfo.Password,
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, loginRes)
|
||||
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeBasicAuthSignup)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, email, verificationRequest.Email)
|
||||
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||
Token: verificationRequest.Token,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty")
|
||||
|
||||
// Using access token update profile
|
||||
s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken))
|
||||
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
|
||||
updateProfileRes, err := resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
|
||||
IsMultiFactorAuthEnabled: refs.NewBoolRef(true),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, updateProfileRes.Message)
|
||||
|
||||
// Login should not return error but access token should be empty as otp should have been sent
|
||||
loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{
|
||||
Email: email,
|
||||
Password: s.TestInfo.Password,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, loginRes)
|
||||
assert.Nil(t, loginRes.AccessToken)
|
||||
|
||||
// Get otp from db
|
||||
otp, err := db.Provider.GetOTPByEmail(ctx, email)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, otp.Otp)
|
||||
|
||||
verifyOtpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
|
||||
Email: email,
|
||||
Otp: otp.Otp,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotEqual(t, verifyOtpRes.AccessToken, "", "access token should not be empty")
|
||||
cleanData(email)
|
||||
})
|
||||
}
|
25
server/utils/generate_otp.go
Normal file
25
server/utils/generate_otp.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GenerateOTP to generate random 6 digit otp
|
||||
func GenerateOTP() string {
|
||||
code := ""
|
||||
codeLength := 6
|
||||
charSet := "ABCDEFGHJKLMNPQRSTUVWXYZ123456789"
|
||||
charSetLength := int32(len(charSet))
|
||||
for i := 0; i < codeLength; i++ {
|
||||
index := randomNumber(0, charSetLength)
|
||||
code += string(charSet[index])
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
func randomNumber(min, max int32) int32 {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
return min + int32(rand.Intn(int(max-min)))
|
||||
}
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// TODO renamae GinContextKey -> GinContext
|
||||
// TODO re-name GinContextKey -> GinContext
|
||||
|
||||
// GinContext to get gin context from context
|
||||
func GinContextFromContext(ctx context.Context) (*gin.Context, error) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
@ -52,6 +53,22 @@ func RegisterEvent(ctx context.Context, eventName string, authRecipe string, use
|
|||
return err
|
||||
}
|
||||
|
||||
// dont trigger webhook call in case of test
|
||||
envKey, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyEnv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if envKey == constants.TestEnv {
|
||||
db.Provider.AddWebhookLog(ctx, models.WebhookLog{
|
||||
HttpStatus: 200,
|
||||
Request: string(requestBody),
|
||||
Response: string(`{"message": "test"}`),
|
||||
WebhookID: webhook.ID,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
requestBytesBuffer := bytes.NewBuffer(requestBody)
|
||||
req, err := http.NewRequest("POST", refs.StringValue(webhook.Endpoint), requestBytesBuffer)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue
Block a user