2021-07-17 16:29:50 +00:00
package resolvers
import (
"context"
2021-07-18 03:55:20 +00:00
"fmt"
2021-07-17 16:29:50 +00:00
"strings"
"time"
2022-05-24 07:12:29 +00:00
log "github.com/sirupsen/logrus"
2021-07-28 10:13:08 +00:00
"github.com/authorizerdev/authorizer/server/constants"
2022-01-22 19:54:41 +00:00
"github.com/authorizerdev/authorizer/server/cookie"
2022-02-28 15:56:49 +00:00
"github.com/authorizerdev/authorizer/server/crypto"
2021-07-23 16:27:44 +00:00
"github.com/authorizerdev/authorizer/server/db"
2022-01-21 08:04:04 +00:00
"github.com/authorizerdev/authorizer/server/db/models"
2022-01-17 06:02:13 +00:00
"github.com/authorizerdev/authorizer/server/email"
2021-07-23 16:27:44 +00:00
"github.com/authorizerdev/authorizer/server/graph/model"
2022-05-27 17:50:38 +00:00
"github.com/authorizerdev/authorizer/server/memorystore"
2022-05-30 06:24:16 +00:00
"github.com/authorizerdev/authorizer/server/parsers"
2022-01-22 19:54:41 +00:00
"github.com/authorizerdev/authorizer/server/token"
2021-07-23 16:27:44 +00:00
"github.com/authorizerdev/authorizer/server/utils"
2022-05-30 06:24:16 +00:00
"github.com/authorizerdev/authorizer/server/validators"
2021-07-17 16:29:50 +00:00
)
2022-01-17 06:02:13 +00:00
// SignupResolver is a resolver for signup mutation
func SignupResolver ( ctx context . Context , params model . SignUpInput ) ( * model . AuthResponse , error ) {
2021-07-28 10:13:08 +00:00
var res * model . AuthResponse
2022-05-24 07:12:29 +00:00
gc , err := utils . GinContextFromContext ( ctx )
2021-07-28 10:13:08 +00:00
if err != nil {
2022-05-25 07:00:22 +00:00
log . Debug ( "Failed to get GinContext: " , err )
2021-07-28 10:13:08 +00:00
return res , err
}
2022-05-30 03:49:55 +00:00
isSignupDisabled , err := memorystore . Provider . GetBoolStoreEnvVariable ( constants . EnvKeyDisableSignUp )
if err != nil {
log . Debug ( "Error getting signup disabled: " , err )
isSignupDisabled = true
}
if isSignupDisabled {
2022-05-25 07:00:22 +00:00
log . Debug ( "Signup is disabled" )
2022-03-16 17:19:18 +00:00
return res , fmt . Errorf ( ` signup is disabled for this instance ` )
}
2022-05-30 03:49:55 +00:00
isBasicAuthDisabled , err := memorystore . Provider . GetBoolStoreEnvVariable ( constants . EnvKeyDisableBasicAuthentication )
if err != nil {
log . Debug ( "Error getting basic auth disabled: " , err )
isBasicAuthDisabled = true
}
if isBasicAuthDisabled {
2022-05-25 07:00:22 +00:00
log . Debug ( "Basic authentication is disabled" )
2021-07-28 10:13:08 +00:00
return res , fmt . Errorf ( ` basic authentication is disabled for this instance ` )
}
2022-03-16 17:19:18 +00:00
2021-07-20 22:04:03 +00:00
if params . ConfirmPassword != params . Password {
2022-05-25 07:00:22 +00:00
log . Debug ( "Passwords do not match" )
2021-12-20 17:51:27 +00:00
return res , fmt . Errorf ( ` password and confirm password does not match ` )
2021-07-17 16:29:50 +00:00
}
2022-05-30 06:24:16 +00:00
if ! validators . IsValidPassword ( params . Password ) {
2022-05-24 07:12:29 +00:00
log . Debug ( "Invalid password" )
2022-03-17 10:05:07 +00:00
return res , fmt . Errorf ( ` password is not valid. It needs to be at least 6 characters long and contain at least one number, one uppercase letter, one lowercase letter and one special character ` )
}
2021-07-17 16:29:50 +00:00
params . Email = strings . ToLower ( params . Email )
2022-05-30 06:24:16 +00:00
if ! validators . IsValidEmail ( params . Email ) {
2022-05-25 07:00:22 +00:00
log . Debug ( "Invalid email: " , params . Email )
2021-07-18 03:55:20 +00:00
return res , fmt . Errorf ( ` invalid email address ` )
2021-07-17 16:29:50 +00:00
}
2022-05-24 07:12:29 +00:00
log := log . WithFields ( log . Fields {
"email" : params . Email ,
} )
2021-07-17 16:29:50 +00:00
// find user with email
2022-01-21 08:04:04 +00:00
existingUser , err := db . Provider . GetUserByEmail ( params . Email )
2021-07-17 16:29:50 +00:00
if err != nil {
2022-05-25 07:00:22 +00:00
log . Debug ( "Failed to get user by email: " , err )
2021-07-17 16:29:50 +00:00
}
2021-12-22 10:08:51 +00:00
if existingUser . EmailVerifiedAt != nil {
2021-07-17 16:29:50 +00:00
// email is verified
2022-05-24 07:12:29 +00:00
log . Debug ( "Email is already verified and signed up." )
2021-12-20 17:51:27 +00:00
return res , fmt . Errorf ( ` %s has already signed up ` , params . Email )
2021-12-22 10:08:51 +00:00
} else if existingUser . ID != "" && existingUser . EmailVerifiedAt == nil {
2022-05-24 07:12:29 +00:00
log . Debug ( "Email is already signed up. Verification pending..." )
2021-12-20 17:51:27 +00:00
return res , fmt . Errorf ( "%s has already signed up. please complete the email verification process or reset the password" , params . Email )
2021-07-17 16:29:50 +00:00
}
2021-12-20 17:51:27 +00:00
2021-12-24 13:12:32 +00:00
inputRoles := [ ] string { }
if len ( params . Roles ) > 0 {
// check if roles exists
2022-05-31 02:44:03 +00:00
rolesString , err := memorystore . Provider . GetStringStoreEnvVariable ( constants . EnvKeyRoles )
roles := [ ] string { }
2022-05-30 03:49:55 +00:00
if err != nil {
log . Debug ( "Error getting roles: " , err )
return res , err
2022-05-31 02:44:03 +00:00
} else {
roles = strings . Split ( rolesString , "," )
2022-05-30 03:49:55 +00:00
}
2022-06-07 02:30:30 +00:00
if ! validators . IsValidRoles ( params . Roles , roles ) {
2022-05-25 07:00:22 +00:00
log . Debug ( "Invalid roles: " , params . Roles )
2021-12-24 13:12:32 +00:00
return res , fmt . Errorf ( ` invalid roles ` )
} else {
inputRoles = params . Roles
}
} else {
2022-05-31 02:44:03 +00:00
inputRolesString , err := memorystore . Provider . GetStringStoreEnvVariable ( constants . EnvKeyDefaultRoles )
2022-05-30 03:49:55 +00:00
if err != nil {
log . Debug ( "Error getting default roles: " , err )
return res , err
2022-05-31 02:44:03 +00:00
} else {
inputRoles = strings . Split ( inputRolesString , "," )
2022-05-30 03:49:55 +00:00
}
2021-12-24 13:12:32 +00:00
}
2022-01-21 08:04:04 +00:00
user := models . User {
2021-07-17 17:09:50 +00:00
Email : params . Email ,
2021-07-17 16:29:50 +00:00
}
2021-09-20 05:06:26 +00:00
user . Roles = strings . Join ( inputRoles , "," )
2022-02-28 15:56:49 +00:00
password , _ := crypto . EncryptPassword ( params . Password )
2021-12-22 10:01:45 +00:00
user . Password = & password
2021-07-17 17:09:50 +00:00
2021-12-22 05:21:12 +00:00
if params . GivenName != nil {
2021-12-22 10:01:45 +00:00
user . GivenName = params . GivenName
2021-07-17 16:29:50 +00:00
}
2021-12-22 05:21:12 +00:00
if params . FamilyName != nil {
2021-12-22 10:01:45 +00:00
user . FamilyName = params . FamilyName
2021-07-17 16:29:50 +00:00
}
2021-12-22 05:21:12 +00:00
if params . MiddleName != nil {
2021-12-22 10:01:45 +00:00
user . MiddleName = params . MiddleName
2021-12-22 05:21:12 +00:00
}
if params . Nickname != nil {
2021-12-22 10:01:45 +00:00
user . Nickname = params . Nickname
2021-12-22 05:21:12 +00:00
}
if params . Gender != nil {
2021-12-22 10:01:45 +00:00
user . Gender = params . Gender
2021-12-22 05:21:12 +00:00
}
if params . Birthdate != nil {
2021-12-22 10:01:45 +00:00
user . Birthdate = params . Birthdate
2021-12-22 05:21:12 +00:00
}
if params . PhoneNumber != nil {
2021-12-22 10:01:45 +00:00
user . PhoneNumber = params . PhoneNumber
2021-12-22 05:21:12 +00:00
}
if params . Picture != nil {
2021-12-22 10:01:45 +00:00
user . Picture = params . Picture
2021-12-22 05:21:12 +00:00
}
2022-01-17 06:02:13 +00:00
user . SignupMethods = constants . SignupMethodBasicAuth
2022-05-30 03:49:55 +00:00
isEmailVerificationDisabled , err := memorystore . Provider . GetBoolStoreEnvVariable ( constants . EnvKeyDisableEmailVerification )
if err != nil {
log . Debug ( "Error getting email verification disabled: " , err )
isEmailVerificationDisabled = true
}
if isEmailVerificationDisabled {
2021-12-22 10:08:51 +00:00
now := time . Now ( ) . Unix ( )
user . EmailVerifiedAt = & now
2021-07-28 10:13:08 +00:00
}
2022-01-21 08:04:04 +00:00
user , err = db . Provider . AddUser ( user )
2021-07-17 16:29:50 +00:00
if err != nil {
2022-05-25 07:00:22 +00:00
log . Debug ( "Failed to add user: " , err )
2021-07-17 16:29:50 +00:00
return res , err
}
2021-10-13 16:41:41 +00:00
roles := strings . Split ( user . Roles , "," )
2022-01-22 19:54:41 +00:00
userToReturn := user . AsAPIUser ( )
2021-07-28 10:13:08 +00:00
2022-05-30 06:24:16 +00:00
hostname := parsers . GetHost ( gc )
2022-05-30 03:49:55 +00:00
if ! isEmailVerificationDisabled {
2021-07-28 10:13:08 +00:00
// insert verification request
2022-03-08 07:06:26 +00:00
_ , nonceHash , err := utils . GenerateNonce ( )
2022-03-02 12:12:31 +00:00
if err != nil {
2022-05-25 07:00:22 +00:00
log . Debug ( "Failed to generate nonce: " , err )
2022-03-02 12:12:31 +00:00
return res , err
}
2022-01-17 06:02:13 +00:00
verificationType := constants . VerificationTypeBasicAuthSignup
2022-05-30 06:24:16 +00:00
redirectURL := parsers . GetAppURL ( gc )
2022-03-16 16:22:45 +00:00
if params . RedirectURI != nil {
redirectURL = * params . RedirectURI
}
2022-03-08 07:06:26 +00:00
verificationToken , err := token . CreateVerificationToken ( params . Email , verificationType , hostname , nonceHash , redirectURL )
2021-07-28 10:13:08 +00:00
if err != nil {
2022-05-25 07:00:22 +00:00
log . Debug ( "Failed to create verification token: " , err )
2022-03-02 12:12:31 +00:00
return res , err
2021-07-28 10:13:08 +00:00
}
2022-05-24 07:12:29 +00:00
_ , err = db . Provider . AddVerificationRequest ( models . VerificationRequest {
2022-03-08 07:06:26 +00:00
Token : verificationToken ,
Identifier : verificationType ,
ExpiresAt : time . Now ( ) . Add ( time . Minute * 30 ) . Unix ( ) ,
Email : params . Email ,
Nonce : nonceHash ,
RedirectURI : redirectURL ,
2021-07-28 10:13:08 +00:00
} )
2022-05-24 07:12:29 +00:00
if err != nil {
2022-05-25 07:00:22 +00:00
log . Debug ( "Failed to add verification request: " , err )
2022-05-24 07:12:29 +00:00
return res , err
}
2021-07-28 10:13:08 +00:00
// exec it as go routin so that we can reduce the api latency
2022-03-02 12:12:31 +00:00
go email . SendVerificationMail ( params . Email , verificationToken , hostname )
2021-07-28 10:13:08 +00:00
res = & model . AuthResponse {
Message : ` Verification email has been sent. Please check your inbox ` ,
User : userToReturn ,
}
} else {
2022-03-02 12:12:31 +00:00
scope := [ ] string { "openid" , "email" , "profile" }
2022-03-09 06:23:34 +00:00
if params . Scope != nil && len ( scope ) > 0 {
scope = params . Scope
}
2021-07-28 10:13:08 +00:00
2022-03-02 12:12:31 +00:00
authToken , err := token . CreateAuthToken ( gc , user , roles , scope )
2022-01-22 19:54:41 +00:00
if err != nil {
2022-05-25 07:00:22 +00:00
log . Debug ( "Failed to create auth token: " , err )
2022-01-22 19:54:41 +00:00
return res , err
}
2022-03-02 12:12:31 +00:00
2022-06-11 13:40:39 +00:00
memorystore . Provider . SetUserSession ( user . ID , authToken . FingerPrintHash , authToken . FingerPrint )
memorystore . Provider . SetUserSession ( user . ID , authToken . AccessToken . Token , authToken . FingerPrint )
if authToken . RefreshToken != nil {
res . RefreshToken = & authToken . RefreshToken . Token
memorystore . Provider . SetUserSession ( user . ID , authToken . RefreshToken . Token , authToken . FingerPrint )
}
2022-03-02 12:12:31 +00:00
cookie . SetSession ( gc , authToken . FingerPrintHash )
2022-04-23 12:22:02 +00:00
go db . Provider . AddSession ( models . Session {
UserID : user . ID ,
UserAgent : utils . GetUserAgent ( gc . Request ) ,
IP : utils . GetIP ( gc . Request ) ,
} )
2022-03-02 12:12:31 +00:00
2022-03-25 12:21:20 +00:00
expiresIn := authToken . AccessToken . ExpiresAt - time . Now ( ) . Unix ( )
if expiresIn <= 0 {
expiresIn = 1
}
2022-01-22 19:54:41 +00:00
2021-07-28 10:13:08 +00:00
res = & model . AuthResponse {
2021-12-22 05:21:12 +00:00
Message : ` Signed up successfully. ` ,
2022-01-22 19:54:41 +00:00
AccessToken : & authToken . AccessToken . Token ,
2022-03-02 12:12:31 +00:00
ExpiresIn : & expiresIn ,
2021-12-22 05:21:12 +00:00
User : userToReturn ,
2021-07-28 10:13:08 +00:00
}
2021-07-17 16:29:50 +00:00
}
return res , nil
}