Compare commits

...

25 Commits

Author SHA1 Message Date
Lakhan Samani
5e6b033024 fix microsoft active directory config 2023-08-17 14:20:31 +05:30
Lakhan Samani
171d4e3fff remove unused code 2023-08-14 14:16:54 +05:30
Lakhan Samani
cf96a0087f Fix tests for verifying otp using mfa session 2023-08-14 14:15:52 +05:30
Lakhan Samani
09cfad9c27 Merge pull request #382 from authorizerdev/feat-add-field-for-app-data
Add app_data
2023-08-14 12:05:58 +05:30
Lakhan Samani
35e563ab3b Add app_data 2023-08-14 12:01:37 +05:30
Lakhan Samani
e625ed9633 allow common tenant for microsoft 2023-08-03 14:43:27 +05:30
Lakhan Samani
a042c202a0 fix microsoft active directory config 2023-08-03 13:29:07 +05:30
Lakhan Samani
7a76b783b1 Merge pull request #372 from catusax/main
feat: add mfa session to secure otp login
2023-08-03 12:34:39 +05:30
Lakhan Samani
e5400bc7bd fix microsoft active directory config 2023-08-03 12:33:20 +05:30
Lakhan Samani
a8503666e3 fix: add events for signup 2023-08-02 10:02:41 +05:30
Lakhan Samani
b028be3cbc Merge pull request #377 from authorizerdev/fix-webhook-test-endpoint
fix: test webhook endpoint mutation
2023-08-02 00:04:55 +05:30
Lakhan Samani
9a8d20b698 fix: test webhook endpoint mutation
Resolves #376
2023-08-02 00:04:07 +05:30
Lakhan Samani
fab3c2f87e Merge pull request #375 from authorizerdev/fix-db-refs
Fix db refs
2023-08-01 23:38:00 +05:30
catusax
0c334856bc Merge branch 'main' into main 2023-07-24 14:04:26 +08:00
catusax
ba0cf189de userid ass mfa session key 2023-07-24 12:00:30 +08:00
Lakhan Samani
9f52c08883 [app] bump authorizer-react 1.1.13 2023-07-24 11:56:56 +08:00
Lakhan Samani
80f3698f06 [app] bump authorizer-react 1.1.12 2023-07-24 11:56:56 +08:00
Lakhan Samani
2a2b7abc08 Add optional show_mobile_otp_screen 2023-07-24 11:56:56 +08:00
Lakhan Samani
27e3ed82e4 Update resend otp 2023-07-24 11:56:55 +08:00
Lakhan Samani
6077702626 fix: tests for otp refactor 2023-07-24 11:56:55 +08:00
Lakhan Samani
cf54fcef03 Fix tests 2023-07-24 11:56:55 +08:00
Lakhan Samani
2f849b8f0c Refactor code for otp 2023-07-24 11:56:55 +08:00
Lakhan Samani
85ca0f09bf [draft] Move sms verificaiton to otp models 2023-07-24 11:55:26 +08:00
catusax
e7652db89c add comments 2023-07-23 13:02:14 +08:00
catusax
5018462559 feat: add mfa session to secure otp login 2023-07-20 15:11:39 +08:00
31 changed files with 1849 additions and 139 deletions

View File

@@ -118,7 +118,6 @@ const Webhooks = () => {
useEffect(() => { useEffect(() => {
fetchWebookData(); fetchWebookData();
}, [paginationProps.page, paginationProps.limit]); }, [paginationProps.page, paginationProps.limit]);
console.log({ webhookData });
return ( return (
<Box m="5" py="5" px="10" bg="white" rounded="md"> <Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center"> <Flex margin="2% 0" justifyContent="space-between" alignItems="center">

View File

@@ -5,4 +5,6 @@ const (
AppCookieName = "cookie" AppCookieName = "cookie"
// AdminCookieName is the name of the cookie that is used to store the admin token // AdminCookieName is the name of the cookie that is used to store the admin token
AdminCookieName = "authorizer-admin" AdminCookieName = "authorizer-admin"
// MfaCookieName is the name of the cookie that is used to store the mfa session
MfaCookieName = "mfa"
) )

View File

@@ -0,0 +1,89 @@
package cookie
import (
"net/http"
"net/url"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers"
"github.com/gin-gonic/gin"
)
// SetMfaSession sets the mfa session cookie in the response
func SetMfaSession(gc *gin.Context, sessionID string) {
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
if err != nil {
log.Debug("Error while getting app cookie secure from env variable: %v", err)
appCookieSecure = true
}
secure := appCookieSecure
httpOnly := appCookieSecure
hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname)
if domain != "localhost" {
domain = "." + domain
}
// Since app cookie can come from cross site it becomes important to set this in lax mode when insecure.
// Example person using custom UI on their app domain and making request to authorizer domain.
// For more information check:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// https://github.com/gin-gonic/gin/blob/master/context.go#L86
// TODO add ability to sameSite = none / strict from dashboard
if !appCookieSecure {
gc.SetSameSite(http.SameSiteLaxMode)
} else {
gc.SetSameSite(http.SameSiteNoneMode)
}
// TODO allow configuring from dashboard
age := 60
gc.SetCookie(constants.MfaCookieName+"_session", sessionID, age, "/", host, secure, httpOnly)
gc.SetCookie(constants.MfaCookieName+"_session_domain", sessionID, age, "/", domain, secure, httpOnly)
}
// DeleteMfaSession deletes the mfa session cookies to expire
func DeleteMfaSession(gc *gin.Context) {
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
if err != nil {
log.Debug("Error while getting app cookie secure from env variable: %v", err)
appCookieSecure = true
}
secure := appCookieSecure
httpOnly := appCookieSecure
hostname := parsers.GetHost(gc)
host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname)
if domain != "localhost" {
domain = "." + domain
}
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(constants.MfaCookieName+"_session", "", -1, "/", host, secure, httpOnly)
gc.SetCookie(constants.MfaCookieName+"_session_domain", "", -1, "/", domain, secure, httpOnly)
}
// GetMfaSession gets the mfa session cookie from context
func GetMfaSession(gc *gin.Context) (string, error) {
var cookie *http.Cookie
var err error
cookie, err = gc.Request.Cookie(constants.MfaCookieName + "_session")
if err != nil {
cookie, err = gc.Request.Cookie(constants.MfaCookieName + "_session_domain")
if err != nil {
return "", err
}
}
decodedValue, err := url.PathUnescape(cookie.Value)
if err != nil {
return "", err
}
return decodedValue, nil
}

View File

@@ -33,12 +33,14 @@ type User struct {
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled" dynamo:"is_multi_factor_auth_enabled"` IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled" dynamo:"is_multi_factor_auth_enabled"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"` UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"` CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
AppData *string `json:"app_data" bson:"app_data" cql:"app_data" dynamo:"app_data"`
} }
func (user *User) AsAPIUser() *model.User { func (user *User) AsAPIUser() *model.User {
isEmailVerified := user.EmailVerifiedAt != nil isEmailVerified := user.EmailVerifiedAt != nil
isPhoneVerified := user.PhoneNumberVerifiedAt != nil isPhoneVerified := user.PhoneNumberVerifiedAt != nil
appDataMap := make(map[string]interface{})
json.Unmarshal([]byte(refs.StringValue(user.AppData)), &appDataMap)
// id := user.ID // id := user.ID
// if strings.Contains(id, Collections.User+"/") { // if strings.Contains(id, Collections.User+"/") {
// id = strings.TrimPrefix(id, Collections.User+"/") // id = strings.TrimPrefix(id, Collections.User+"/")
@@ -63,6 +65,7 @@ func (user *User) AsAPIUser() *model.User {
IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled, IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled,
CreatedAt: refs.NewInt64Ref(user.CreatedAt), CreatedAt: refs.NewInt64Ref(user.CreatedAt),
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt), UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
AppData: appDataMap,
} }
} }

View File

@@ -74,7 +74,6 @@ func (p *provider) ListVerificationRequests(ctx context.Context, pagination *mod
var verificationRequest models.VerificationRequest var verificationRequest models.VerificationRequest
err := scanner.Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt) err := scanner.Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
if err != nil { if err != nil {
fmt.Println("=> getting error here...", err)
return nil, err return nil, err
} }
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest()) verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())

View File

@@ -72,7 +72,6 @@ func getEmailTemplate(event string, data map[string]interface{}) (*model.EmailTe
return nil, err return nil, err
} }
subjectString := buf.String() subjectString := buf.String()
return &model.EmailTemplate{ return &model.EmailTemplate{
Template: templateString, Template: templateString,
Subject: subjectString, Subject: subjectString,

View File

@@ -30,7 +30,7 @@ require (
go.mongodb.org/mongo-driver v1.8.1 go.mongodb.org/mongo-driver v1.8.1
golang.org/x/crypto v0.4.0 golang.org/x/crypto v0.4.0
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/mail.v2 v2.3.1 gopkg.in/mail.v2 v2.3.1

File diff suppressed because it is too large Load Diff

View File

@@ -245,6 +245,7 @@ type ComplexityRoot struct {
} }
User struct { User struct {
AppData func(childComplexity int) int
Birthdate func(childComplexity int) int Birthdate func(childComplexity int) int
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
Email func(childComplexity int) int Email func(childComplexity int) int
@@ -1695,6 +1696,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.TestEndpointResponse.Response(childComplexity), true return e.complexity.TestEndpointResponse.Response(childComplexity), true
case "User.app_data":
if e.complexity.User.AppData == nil {
break
}
return e.complexity.User.AppData(childComplexity), true
case "User.birthdate": case "User.birthdate":
if e.complexity.User.Birthdate == nil { if e.complexity.User.Birthdate == nil {
break break
@@ -2229,6 +2237,7 @@ type User {
updated_at: Int64 updated_at: Int64
revoked_timestamp: Int64 revoked_timestamp: Int64
is_multi_factor_auth_enabled: Boolean is_multi_factor_auth_enabled: Boolean
app_data: Map
} }
type Users { type Users {
@@ -2500,6 +2509,7 @@ input MobileSignUpInput {
# it is used to get code for an on-going auth process during login # it is used to get code for an on-going auth process during login
# and use that code for setting ` + "`" + `c_hash` + "`" + ` in id_token # and use that code for setting ` + "`" + `c_hash` + "`" + ` in id_token
state: String state: String
app_data: Map
} }
input SignUpInput { input SignUpInput {
@@ -2522,6 +2532,7 @@ input SignUpInput {
# it is used to get code for an on-going auth process during login # it is used to get code for an on-going auth process during login
# and use that code for setting ` + "`" + `c_hash` + "`" + ` in id_token # and use that code for setting ` + "`" + `c_hash` + "`" + ` in id_token
state: String state: String
app_data: Map
} }
input LoginInput { input LoginInput {
@@ -2577,6 +2588,7 @@ input UpdateProfileInput {
phone_number: String phone_number: String
picture: String picture: String
is_multi_factor_auth_enabled: Boolean is_multi_factor_auth_enabled: Boolean
app_data: Map
} }
input UpdateUserInput { input UpdateUserInput {
@@ -2593,6 +2605,7 @@ input UpdateUserInput {
picture: String picture: String
roles: [String] roles: [String]
is_multi_factor_auth_enabled: Boolean is_multi_factor_auth_enabled: Boolean
app_data: Map
} }
input ForgotPasswordInput { input ForgotPasswordInput {
@@ -2690,6 +2703,7 @@ input WebhookRequest {
input TestEndpointRequest { input TestEndpointRequest {
endpoint: String! endpoint: String!
event_name: String! event_name: String!
event_description: String
headers: Map headers: Map
} }
@@ -3803,6 +3817,8 @@ func (ec *executionContext) fieldContext_AuthResponse_user(ctx context.Context,
return ec.fieldContext_User_revoked_timestamp(ctx, field) return ec.fieldContext_User_revoked_timestamp(ctx, field)
case "is_multi_factor_auth_enabled": case "is_multi_factor_auth_enabled":
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field) return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type User", field.Name) return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
}, },
@@ -7098,6 +7114,8 @@ func (ec *executionContext) fieldContext_InviteMembersResponse_Users(ctx context
return ec.fieldContext_User_revoked_timestamp(ctx, field) return ec.fieldContext_User_revoked_timestamp(ctx, field)
case "is_multi_factor_auth_enabled": case "is_multi_factor_auth_enabled":
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field) return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type User", field.Name) return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
}, },
@@ -8800,6 +8818,8 @@ func (ec *executionContext) fieldContext_Mutation__update_user(ctx context.Conte
return ec.fieldContext_User_revoked_timestamp(ctx, field) return ec.fieldContext_User_revoked_timestamp(ctx, field)
case "is_multi_factor_auth_enabled": case "is_multi_factor_auth_enabled":
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field) return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type User", field.Name) return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
}, },
@@ -10102,6 +10122,8 @@ func (ec *executionContext) fieldContext_Query_profile(ctx context.Context, fiel
return ec.fieldContext_User_revoked_timestamp(ctx, field) return ec.fieldContext_User_revoked_timestamp(ctx, field)
case "is_multi_factor_auth_enabled": case "is_multi_factor_auth_enabled":
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field) return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type User", field.Name) return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
}, },
@@ -10367,6 +10389,8 @@ func (ec *executionContext) fieldContext_Query__user(ctx context.Context, field
return ec.fieldContext_User_revoked_timestamp(ctx, field) return ec.fieldContext_User_revoked_timestamp(ctx, field)
case "is_multi_factor_auth_enabled": case "is_multi_factor_auth_enabled":
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field) return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type User", field.Name) return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
}, },
@@ -12228,6 +12252,47 @@ func (ec *executionContext) fieldContext_User_is_multi_factor_auth_enabled(ctx c
return fc, nil return fc, nil
} }
func (ec *executionContext) _User_app_data(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_User_app_data(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.AppData, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(map[string]interface{})
fc.Result = res
return ec.marshalOMap2map(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_User_app_data(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "User",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Map does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Users_pagination(ctx context.Context, field graphql.CollectedField, obj *model.Users) (ret graphql.Marshaler) { func (ec *executionContext) _Users_pagination(ctx context.Context, field graphql.CollectedField, obj *model.Users) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Users_pagination(ctx, field) fc, err := ec.fieldContext_Users_pagination(ctx, field)
if err != nil { if err != nil {
@@ -12359,6 +12424,8 @@ func (ec *executionContext) fieldContext_Users_users(ctx context.Context, field
return ec.fieldContext_User_revoked_timestamp(ctx, field) return ec.fieldContext_User_revoked_timestamp(ctx, field)
case "is_multi_factor_auth_enabled": case "is_multi_factor_auth_enabled":
return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field) return ec.fieldContext_User_is_multi_factor_auth_enabled(ctx, field)
case "app_data":
return ec.fieldContext_User_app_data(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type User", field.Name) return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
}, },
@@ -16200,7 +16267,7 @@ func (ec *executionContext) unmarshalInputMobileSignUpInput(ctx context.Context,
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "password", "confirm_password", "roles", "scope", "redirect_uri", "is_multi_factor_auth_enabled", "state"} fieldsInOrder := [...]string{"email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "password", "confirm_password", "roles", "scope", "redirect_uri", "is_multi_factor_auth_enabled", "state", "app_data"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@@ -16335,6 +16402,14 @@ func (ec *executionContext) unmarshalInputMobileSignUpInput(ctx context.Context,
if err != nil { if err != nil {
return it, err return it, err
} }
case "app_data":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("app_data"))
it.AppData, err = ec.unmarshalOMap2map(ctx, v)
if err != nil {
return it, err
}
} }
} }
@@ -16608,7 +16683,7 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "password", "confirm_password", "roles", "scope", "redirect_uri", "is_multi_factor_auth_enabled", "state"} fieldsInOrder := [...]string{"email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "password", "confirm_password", "roles", "scope", "redirect_uri", "is_multi_factor_auth_enabled", "state", "app_data"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@@ -16743,6 +16818,14 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
if err != nil { if err != nil {
return it, err return it, err
} }
case "app_data":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("app_data"))
it.AppData, err = ec.unmarshalOMap2map(ctx, v)
if err != nil {
return it, err
}
} }
} }
@@ -16756,7 +16839,7 @@ func (ec *executionContext) unmarshalInputTestEndpointRequest(ctx context.Contex
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"endpoint", "event_name", "headers"} fieldsInOrder := [...]string{"endpoint", "event_name", "event_description", "headers"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@@ -16779,6 +16862,14 @@ func (ec *executionContext) unmarshalInputTestEndpointRequest(ctx context.Contex
if err != nil { if err != nil {
return it, err return it, err
} }
case "event_description":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("event_description"))
it.EventDescription, err = ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
case "headers": case "headers":
var err error var err error
@@ -17324,7 +17415,7 @@ func (ec *executionContext) unmarshalInputUpdateProfileInput(ctx context.Context
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"old_password", "new_password", "confirm_new_password", "email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "is_multi_factor_auth_enabled"} fieldsInOrder := [...]string{"old_password", "new_password", "confirm_new_password", "email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "is_multi_factor_auth_enabled", "app_data"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@@ -17435,6 +17526,14 @@ func (ec *executionContext) unmarshalInputUpdateProfileInput(ctx context.Context
if err != nil { if err != nil {
return it, err return it, err
} }
case "app_data":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("app_data"))
it.AppData, err = ec.unmarshalOMap2map(ctx, v)
if err != nil {
return it, err
}
} }
} }
@@ -17448,7 +17547,7 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"id", "email", "email_verified", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "roles", "is_multi_factor_auth_enabled"} fieldsInOrder := [...]string{"id", "email", "email_verified", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "roles", "is_multi_factor_auth_enabled", "app_data"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@@ -17559,6 +17658,14 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o
if err != nil { if err != nil {
return it, err return it, err
} }
case "app_data":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("app_data"))
it.AppData, err = ec.unmarshalOMap2map(ctx, v)
if err != nil {
return it, err
}
} }
} }
@@ -19465,6 +19572,10 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._User_is_multi_factor_auth_enabled(ctx, field, obj) out.Values[i] = ec._User_is_multi_factor_auth_enabled(ctx, field, obj)
case "app_data":
out.Values[i] = ec._User_app_data(ctx, field, obj)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }

View File

@@ -207,22 +207,23 @@ type MobileLoginInput struct {
} }
type MobileSignUpInput struct { type MobileSignUpInput struct {
Email *string `json:"email"` Email *string `json:"email"`
GivenName *string `json:"given_name"` GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"` FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"` MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"` Nickname *string `json:"nickname"`
Gender *string `json:"gender"` Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"` Birthdate *string `json:"birthdate"`
PhoneNumber string `json:"phone_number"` PhoneNumber string `json:"phone_number"`
Picture *string `json:"picture"` Picture *string `json:"picture"`
Password string `json:"password"` Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"` ConfirmPassword string `json:"confirm_password"`
Roles []string `json:"roles"` Roles []string `json:"roles"`
Scope []string `json:"scope"` Scope []string `json:"scope"`
RedirectURI *string `json:"redirect_uri"` RedirectURI *string `json:"redirect_uri"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"` IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
State *string `json:"state"` State *string `json:"state"`
AppData map[string]interface{} `json:"app_data"`
} }
type OAuthRevokeInput struct { type OAuthRevokeInput struct {
@@ -282,28 +283,30 @@ type SessionQueryInput struct {
} }
type SignUpInput struct { type SignUpInput struct {
Email string `json:"email"` Email string `json:"email"`
GivenName *string `json:"given_name"` GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"` FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"` MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"` Nickname *string `json:"nickname"`
Gender *string `json:"gender"` Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"` Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"` PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"` Picture *string `json:"picture"`
Password string `json:"password"` Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"` ConfirmPassword string `json:"confirm_password"`
Roles []string `json:"roles"` Roles []string `json:"roles"`
Scope []string `json:"scope"` Scope []string `json:"scope"`
RedirectURI *string `json:"redirect_uri"` RedirectURI *string `json:"redirect_uri"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"` IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
State *string `json:"state"` State *string `json:"state"`
AppData map[string]interface{} `json:"app_data"`
} }
type TestEndpointRequest struct { type TestEndpointRequest struct {
Endpoint string `json:"endpoint"` Endpoint string `json:"endpoint"`
EventName string `json:"event_name"` EventName string `json:"event_name"`
Headers map[string]interface{} `json:"headers"` EventDescription *string `json:"event_description"`
Headers map[string]interface{} `json:"headers"`
} }
type TestEndpointResponse struct { type TestEndpointResponse struct {
@@ -379,35 +382,37 @@ type UpdateEnvInput struct {
} }
type UpdateProfileInput struct { type UpdateProfileInput struct {
OldPassword *string `json:"old_password"` OldPassword *string `json:"old_password"`
NewPassword *string `json:"new_password"` NewPassword *string `json:"new_password"`
ConfirmNewPassword *string `json:"confirm_new_password"` ConfirmNewPassword *string `json:"confirm_new_password"`
Email *string `json:"email"` Email *string `json:"email"`
GivenName *string `json:"given_name"` GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"` FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"` MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"` Nickname *string `json:"nickname"`
Gender *string `json:"gender"` Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"` Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"` PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"` Picture *string `json:"picture"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"` IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
AppData map[string]interface{} `json:"app_data"`
} }
type UpdateUserInput struct { type UpdateUserInput struct {
ID string `json:"id"` ID string `json:"id"`
Email *string `json:"email"` Email *string `json:"email"`
EmailVerified *bool `json:"email_verified"` EmailVerified *bool `json:"email_verified"`
GivenName *string `json:"given_name"` GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"` FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"` MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"` Nickname *string `json:"nickname"`
Gender *string `json:"gender"` Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"` Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"` PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"` Picture *string `json:"picture"`
Roles []*string `json:"roles"` Roles []*string `json:"roles"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"` IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
AppData map[string]interface{} `json:"app_data"`
} }
type UpdateWebhookRequest struct { type UpdateWebhookRequest struct {
@@ -420,25 +425,26 @@ type UpdateWebhookRequest struct {
} }
type User struct { type User struct {
ID string `json:"id"` ID string `json:"id"`
Email string `json:"email"` Email string `json:"email"`
EmailVerified bool `json:"email_verified"` EmailVerified bool `json:"email_verified"`
SignupMethods string `json:"signup_methods"` SignupMethods string `json:"signup_methods"`
GivenName *string `json:"given_name"` GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"` FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"` MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"` Nickname *string `json:"nickname"`
PreferredUsername *string `json:"preferred_username"` PreferredUsername *string `json:"preferred_username"`
Gender *string `json:"gender"` Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"` Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"` PhoneNumber *string `json:"phone_number"`
PhoneNumberVerified *bool `json:"phone_number_verified"` PhoneNumberVerified *bool `json:"phone_number_verified"`
Picture *string `json:"picture"` Picture *string `json:"picture"`
Roles []string `json:"roles"` Roles []string `json:"roles"`
CreatedAt *int64 `json:"created_at"` CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"` UpdatedAt *int64 `json:"updated_at"`
RevokedTimestamp *int64 `json:"revoked_timestamp"` RevokedTimestamp *int64 `json:"revoked_timestamp"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"` IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
AppData map[string]interface{} `json:"app_data"`
} }
type Users struct { type Users struct {

View File

@@ -51,6 +51,7 @@ type User {
updated_at: Int64 updated_at: Int64
revoked_timestamp: Int64 revoked_timestamp: Int64
is_multi_factor_auth_enabled: Boolean is_multi_factor_auth_enabled: Boolean
app_data: Map
} }
type Users { type Users {
@@ -322,6 +323,7 @@ input MobileSignUpInput {
# it is used to get code for an on-going auth process during login # it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token # and use that code for setting `c_hash` in id_token
state: String state: String
app_data: Map
} }
input SignUpInput { input SignUpInput {
@@ -344,6 +346,7 @@ input SignUpInput {
# it is used to get code for an on-going auth process during login # it is used to get code for an on-going auth process during login
# and use that code for setting `c_hash` in id_token # and use that code for setting `c_hash` in id_token
state: String state: String
app_data: Map
} }
input LoginInput { input LoginInput {
@@ -399,6 +402,7 @@ input UpdateProfileInput {
phone_number: String phone_number: String
picture: String picture: String
is_multi_factor_auth_enabled: Boolean is_multi_factor_auth_enabled: Boolean
app_data: Map
} }
input UpdateUserInput { input UpdateUserInput {
@@ -415,6 +419,7 @@ input UpdateUserInput {
picture: String picture: String
roles: [String] roles: [String]
is_multi_factor_auth_enabled: Boolean is_multi_factor_auth_enabled: Boolean
app_data: Map
} }
input ForgotPasswordInput { input ForgotPasswordInput {
@@ -512,6 +517,7 @@ input WebhookRequest {
input TestEndpointRequest { input TestEndpointRequest {
endpoint: String! endpoint: String!
event_name: String! event_name: String!
event_description: String
headers: Map headers: Map
} }

View File

@@ -32,11 +32,11 @@ func OAuthCallbackHandler() gin.HandlerFunc {
return func(ctx *gin.Context) { return func(ctx *gin.Context) {
provider := ctx.Param("oauth_provider") provider := ctx.Param("oauth_provider")
state := ctx.Request.FormValue("state") state := ctx.Request.FormValue("state")
sessionState, err := memorystore.Provider.GetState(state) sessionState, err := memorystore.Provider.GetState(state)
if sessionState == "" || err != nil { if sessionState == "" || err != nil {
log.Debug("Invalid oauth state: ", state) log.Debug("Invalid oauth state: ", state)
ctx.JSON(400, gin.H{"error": "invalid oauth state"}) ctx.JSON(400, gin.H{"error": "invalid oauth state"})
return
} }
// contains random token, redirect url, role // contains random token, redirect url, role
sessionSplit := strings.Split(state, "___") sessionSplit := strings.Split(state, "___")
@@ -46,32 +46,34 @@ func OAuthCallbackHandler() gin.HandlerFunc {
ctx.JSON(400, gin.H{"error": "invalid redirect url"}) ctx.JSON(400, gin.H{"error": "invalid redirect url"})
return return
} }
// remove state from store // remove state from store
go memorystore.Provider.RemoveState(state) go memorystore.Provider.RemoveState(state)
stateValue := sessionSplit[0] stateValue := sessionSplit[0]
redirectURL := sessionSplit[1] redirectURL := sessionSplit[1]
inputRoles := strings.Split(sessionSplit[2], ",") inputRoles := strings.Split(sessionSplit[2], ",")
scopes := strings.Split(sessionSplit[3], ",") scopes := strings.Split(sessionSplit[3], ",")
var user *models.User var user *models.User
oauthCode := ctx.Request.FormValue("code") oauthCode := ctx.Request.FormValue("code")
if oauthCode == "" {
log.Debug("Invalid oauth code: ", oauthCode)
ctx.JSON(400, gin.H{"error": "invalid oauth code"})
return
}
switch provider { switch provider {
case constants.AuthRecipeMethodGoogle: case constants.AuthRecipeMethodGoogle:
user, err = processGoogleUserInfo(oauthCode) user, err = processGoogleUserInfo(ctx, oauthCode)
case constants.AuthRecipeMethodGithub: case constants.AuthRecipeMethodGithub:
user, err = processGithubUserInfo(oauthCode) user, err = processGithubUserInfo(ctx, oauthCode)
case constants.AuthRecipeMethodFacebook: case constants.AuthRecipeMethodFacebook:
user, err = processFacebookUserInfo(oauthCode) user, err = processFacebookUserInfo(ctx, oauthCode)
case constants.AuthRecipeMethodLinkedIn: case constants.AuthRecipeMethodLinkedIn:
user, err = processLinkedInUserInfo(oauthCode) user, err = processLinkedInUserInfo(ctx, oauthCode)
case constants.AuthRecipeMethodApple: case constants.AuthRecipeMethodApple:
user, err = processAppleUserInfo(oauthCode) user, err = processAppleUserInfo(ctx, oauthCode)
case constants.AuthRecipeMethodTwitter: case constants.AuthRecipeMethodTwitter:
user, err = processTwitterUserInfo(oauthCode, sessionState) user, err = processTwitterUserInfo(ctx, oauthCode, sessionState)
case constants.AuthRecipeMethodMicrosoft: case constants.AuthRecipeMethodMicrosoft:
user, err = processMicrosoftUserInfo(oauthCode) user, err = processMicrosoftUserInfo(ctx, oauthCode)
default: default:
log.Info("Invalid oauth provider") log.Info("Invalid oauth provider")
err = fmt.Errorf(`invalid oauth provider`) err = fmt.Errorf(`invalid oauth provider`)
@@ -260,6 +262,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
go func() { go func() {
if isSignUp { if isSignUp {
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, provider, user) utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, provider, user)
// User is also logged in with signup
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, provider, user)
} else { } else {
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, provider, user) utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, provider, user)
} }
@@ -279,9 +283,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
} }
} }
func processGoogleUserInfo(code string) (*models.User, error) { func processGoogleUserInfo(ctx context.Context, code string) (*models.User, error) {
var user *models.User var user *models.User
ctx := context.Background()
oauth2Token, err := oauth.OAuthProviders.GoogleConfig.Exchange(ctx, code) oauth2Token, err := oauth.OAuthProviders.GoogleConfig.Exchange(ctx, code)
if err != nil { if err != nil {
log.Debug("Failed to exchange code for token: ", err) log.Debug("Failed to exchange code for token: ", err)
@@ -311,9 +314,9 @@ func processGoogleUserInfo(code string) (*models.User, error) {
return user, nil return user, nil
} }
func processGithubUserInfo(code string) (*models.User, error) { func processGithubUserInfo(ctx context.Context, code string) (*models.User, error) {
var user *models.User var user *models.User
oauth2Token, err := oauth.OAuthProviders.GithubConfig.Exchange(context.TODO(), code) oauth2Token, err := oauth.OAuthProviders.GithubConfig.Exchange(ctx, code)
if err != nil { if err != nil {
log.Debug("Failed to exchange code for token: ", err) log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid github exchange code: %s", err.Error()) return user, fmt.Errorf("invalid github exchange code: %s", err.Error())
@@ -418,9 +421,9 @@ func processGithubUserInfo(code string) (*models.User, error) {
return user, nil return user, nil
} }
func processFacebookUserInfo(code string) (*models.User, error) { func processFacebookUserInfo(ctx context.Context, code string) (*models.User, error) {
var user *models.User var user *models.User
oauth2Token, err := oauth.OAuthProviders.FacebookConfig.Exchange(context.TODO(), code) oauth2Token, err := oauth.OAuthProviders.FacebookConfig.Exchange(ctx, code)
if err != nil { if err != nil {
log.Debug("Invalid facebook exchange code: ", err) log.Debug("Invalid facebook exchange code: ", err)
return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error()) return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
@@ -469,9 +472,9 @@ func processFacebookUserInfo(code string) (*models.User, error) {
return user, nil return user, nil
} }
func processLinkedInUserInfo(code string) (*models.User, error) { func processLinkedInUserInfo(ctx context.Context, code string) (*models.User, error) {
var user *models.User var user *models.User
oauth2Token, err := oauth.OAuthProviders.LinkedInConfig.Exchange(context.TODO(), code) oauth2Token, err := oauth.OAuthProviders.LinkedInConfig.Exchange(ctx, code)
if err != nil { if err != nil {
log.Debug("Failed to exchange code for token: ", err) log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid linkedin exchange code: %s", err.Error()) return user, fmt.Errorf("invalid linkedin exchange code: %s", err.Error())
@@ -551,9 +554,9 @@ func processLinkedInUserInfo(code string) (*models.User, error) {
return user, nil return user, nil
} }
func processAppleUserInfo(code string) (*models.User, error) { func processAppleUserInfo(ctx context.Context, code string) (*models.User, error) {
var user *models.User var user *models.User
oauth2Token, err := oauth.OAuthProviders.AppleConfig.Exchange(context.TODO(), code) oauth2Token, err := oauth.OAuthProviders.AppleConfig.Exchange(ctx, code)
if err != nil { if err != nil {
log.Debug("Failed to exchange code for token: ", err) log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid apple exchange code: %s", err.Error()) return user, fmt.Errorf("invalid apple exchange code: %s", err.Error())
@@ -604,9 +607,9 @@ func processAppleUserInfo(code string) (*models.User, error) {
return user, err return user, err
} }
func processTwitterUserInfo(code, verifier string) (*models.User, error) { func processTwitterUserInfo(ctx context.Context, code, verifier string) (*models.User, error) {
var user *models.User var user *models.User
oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(context.TODO(), code, oauth2.SetAuthURLParam("code_verifier", verifier)) oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(ctx, code, oauth2.SetAuthURLParam("code_verifier", verifier))
if err != nil { if err != nil {
log.Debug("Failed to exchange code for token: ", err) log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid twitter exchange code: %s", err.Error()) return user, fmt.Errorf("invalid twitter exchange code: %s", err.Error())
@@ -672,24 +675,24 @@ func processTwitterUserInfo(code, verifier string) (*models.User, error) {
} }
// process microsoft user information // process microsoft user information
func processMicrosoftUserInfo(code string) (*models.User, error) { func processMicrosoftUserInfo(ctx context.Context, code string) (*models.User, error) {
var user *models.User var user *models.User
ctx := context.Background()
oauth2Token, err := oauth.OAuthProviders.MicrosoftConfig.Exchange(ctx, code) oauth2Token, err := oauth.OAuthProviders.MicrosoftConfig.Exchange(ctx, code)
if err != nil { if err != nil {
log.Debug("Failed to exchange code for token: ", err) log.Debug("Failed to exchange code for token: ", err)
return user, fmt.Errorf("invalid google exchange code: %s", err.Error()) return user, fmt.Errorf("invalid microsoft exchange code: %s", err.Error())
} }
// we need to skip issuer check because for common tenant it will return internal issuer which does not match
verifier := oauth.OIDCProviders.MicrosoftOIDC.Verifier(&oidc.Config{ClientID: oauth.OAuthProviders.MicrosoftConfig.ClientID}) verifier := oauth.OIDCProviders.MicrosoftOIDC.Verifier(&oidc.Config{
ClientID: oauth.OAuthProviders.MicrosoftConfig.ClientID,
SkipIssuerCheck: true,
})
// Extract the ID Token from OAuth2 token. // Extract the ID Token from OAuth2 token.
rawIDToken, ok := oauth2Token.Extra("id_token").(string) rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok { if !ok {
log.Debug("Failed to extract ID Token from OAuth2 token") log.Debug("Failed to extract ID Token from OAuth2 token")
return user, fmt.Errorf("unable to extract id_token") return user, fmt.Errorf("unable to extract id_token")
} }
// Parse and verify ID Token payload. // Parse and verify ID Token payload.
idToken, err := verifier.Verify(ctx, rawIDToken) idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil { if err != nil {

View File

@@ -175,6 +175,8 @@ func VerifyEmailHandler() gin.HandlerFunc {
go func() { go func() {
if isSignUp { if isSignUp {
utils.RegisterEvent(c, constants.UserSignUpWebhookEvent, loginMethod, user) utils.RegisterEvent(c, constants.UserSignUpWebhookEvent, loginMethod, user)
// User is also logged in with signup
utils.RegisterEvent(c, constants.UserLoginWebhookEvent, loginMethod, user)
} else { } else {
utils.RegisterEvent(c, constants.UserLoginWebhookEvent, loginMethod, user) utils.RegisterEvent(c, constants.UserLoginWebhookEvent, loginMethod, user)
} }

View File

@@ -7,18 +7,20 @@ import (
) )
type provider struct { type provider struct {
mutex sync.Mutex mutex sync.Mutex
sessionStore *stores.SessionStore sessionStore *stores.SessionStore
stateStore *stores.StateStore mfasessionStore *stores.SessionStore
envStore *stores.EnvStore stateStore *stores.StateStore
envStore *stores.EnvStore
} }
// NewInMemoryStore returns a new in-memory store. // NewInMemoryStore returns a new in-memory store.
func NewInMemoryProvider() (*provider, error) { func NewInMemoryProvider() (*provider, error) {
return &provider{ return &provider{
mutex: sync.Mutex{}, mutex: sync.Mutex{},
envStore: stores.NewEnvStore(), envStore: stores.NewEnvStore(),
sessionStore: stores.NewSessionStore(), sessionStore: stores.NewSessionStore(),
stateStore: stores.NewStateStore(), mfasessionStore: stores.NewSessionStore(),
stateStore: stores.NewStateStore(),
}, nil }, nil
} }

View File

@@ -42,6 +42,27 @@ func (c *provider) DeleteSessionForNamespace(namespace string) error {
return nil return nil
} }
// SetMfaSession sets the mfa session with key and value of userId
func (c *provider) SetMfaSession(userId, key string, expiration int64) error {
c.mfasessionStore.Set(userId, key, userId, expiration)
return nil
}
// GetMfaSession returns value of given mfa session
func (c *provider) GetMfaSession(userId, key string) (string, error) {
val := c.mfasessionStore.Get(userId, key)
if val == "" {
return "", fmt.Errorf("Not found")
}
return val, nil
}
// DeleteMfaSession deletes given mfa session from in-memory store.
func (c *provider) DeleteMfaSession(userId, key string) error {
c.mfasessionStore.Remove(userId, key)
return nil
}
// SetState sets the state in the in-memory store. // SetState sets the state in the in-memory store.
func (c *provider) SetState(key, state string) error { func (c *provider) SetState(key, state string) error {
if os.Getenv("ENV") != constants.TestEnv { if os.Getenv("ENV") != constants.TestEnv {

View File

@@ -112,4 +112,15 @@ func ProviderTests(t *testing.T, p Provider) {
key, err = p.GetUserSession("auth_provider1:124", "access_token_key") key, err = p.GetUserSession("auth_provider1:124", "access_token_key")
assert.Empty(t, key) assert.Empty(t, key)
assert.Error(t, err) assert.Error(t, err)
err = p.SetMfaSession("auth_provider:123", "session123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
key, err = p.GetMfaSession("auth_provider:123", "session123")
assert.NoError(t, err)
assert.Equal(t, "auth_provider:123", key)
err = p.DeleteMfaSession("auth_provider:123", "session123")
assert.NoError(t, err)
key, err = p.GetMfaSession("auth_provider:123", "session123")
assert.Error(t, err)
assert.Empty(t, key)
} }

View File

@@ -12,6 +12,12 @@ type Provider interface {
DeleteAllUserSessions(userId string) error DeleteAllUserSessions(userId string) error
// DeleteSessionForNamespace deletes the session for a given namespace // DeleteSessionForNamespace deletes the session for a given namespace
DeleteSessionForNamespace(namespace string) error DeleteSessionForNamespace(namespace string) error
// SetMfaSession sets the mfa session with key and value of userId
SetMfaSession(userId, key string, expiration int64) error
// GetMfaSession returns value of given mfa session
GetMfaSession(userId, key string) (string, error)
// DeleteMfaSession deletes given mfa session from in-memory store.
DeleteMfaSession(userId, key string) error
// SetState sets the login state (key, value form) in the session store // SetState sets the login state (key, value form) in the session store
SetState(key, state string) error SetState(key, state string) error

View File

@@ -16,6 +16,8 @@ var (
envStorePrefix = "authorizer_env" envStorePrefix = "authorizer_env"
) )
const mfaSessionPrefix = "mfa_sess_"
// SetUserSession sets the user session for given user identifier in form recipe:user_id // SetUserSession sets the user session for given user identifier in form recipe:user_id
func (c *provider) SetUserSession(userId, key, token string, expiration int64) error { func (c *provider) SetUserSession(userId, key, token string, expiration int64) error {
currentTime := time.Now() currentTime := time.Now()
@@ -91,6 +93,37 @@ func (c *provider) DeleteSessionForNamespace(namespace string) error {
return nil return nil
} }
// SetMfaSession sets the mfa session with key and value of userId
func (c *provider) SetMfaSession(userId, key string, expiration int64) error {
currentTime := time.Now()
expireTime := time.Unix(expiration, 0)
duration := expireTime.Sub(currentTime)
err := c.store.Set(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key), userId, duration).Err()
if err != nil {
log.Debug("Error saving user session to redis: ", err)
return err
}
return nil
}
// GetMfaSession returns value of given mfa session
func (c *provider) GetMfaSession(userId, key string) (string, error) {
data, err := c.store.Get(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key)).Result()
if err != nil {
return "", err
}
return data, nil
}
// DeleteMfaSession deletes given mfa session from in-memory store.
func (c *provider) DeleteMfaSession(userId, key string) error {
if err := c.store.Del(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err)
// continue
}
return nil
}
// SetState sets the state in redis store. // SetState sets the state in redis store.
func (c *provider) SetState(key, value string) error { func (c *provider) SetState(key, value string) error {
err := c.store.Set(c.ctx, stateStorePrefix+key, value, 0).Err() err := c.store.Set(c.ctx, stateStorePrefix+key, value, 0).Err()

View File

@@ -10,11 +10,16 @@ import (
githubOAuth2 "golang.org/x/oauth2/github" githubOAuth2 "golang.org/x/oauth2/github"
linkedInOAuth2 "golang.org/x/oauth2/linkedin" linkedInOAuth2 "golang.org/x/oauth2/linkedin"
microsoftOAuth2 "golang.org/x/oauth2/microsoft" microsoftOAuth2 "golang.org/x/oauth2/microsoft"
"google.golang.org/appengine/log"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
) )
const (
microsoftCommonTenant = "common"
)
// OAuthProviders is a struct that contains reference all the OAuth providers // OAuthProviders is a struct that contains reference all the OAuth providers
type OAuthProvider struct { type OAuthProvider struct {
GoogleConfig *oauth2.Config GoogleConfig *oauth2.Config
@@ -171,12 +176,16 @@ func InitOAuth() error {
microsoftClientSecret = "" microsoftClientSecret = ""
} }
microsoftActiveDirTenantID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftActiveDirectoryTenantID) microsoftActiveDirTenantID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftActiveDirectoryTenantID)
if err != nil { if err != nil || microsoftActiveDirTenantID == "" {
microsoftActiveDirTenantID = "common" microsoftActiveDirTenantID = microsoftCommonTenant
} }
if microsoftClientID != "" && microsoftClientSecret != "" && microsoftActiveDirTenantID != "" { if microsoftClientID != "" && microsoftClientSecret != "" {
if microsoftActiveDirTenantID == microsoftCommonTenant {
ctx = oidc.InsecureIssuerURLContext(ctx, fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", microsoftActiveDirTenantID))
}
p, err := oidc.NewProvider(ctx, fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", microsoftActiveDirTenantID)) p, err := oidc.NewProvider(ctx, fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", microsoftActiveDirTenantID))
if err != nil { if err != nil {
log.Debugf(ctx, "Error while creating OIDC provider for Microsoft: %v", err)
return err return err
} }
OIDCProviders.MicrosoftOIDC = p OIDCProviders.MicrosoftOIDC = p

View File

@@ -113,16 +113,25 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
// If email service is not enabled continue the process in any way // If email service is not enabled continue the process in any way
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && isEmailServiceEnabled && !isMFADisabled { if refs.BoolValue(user.IsMultiFactorAuthEnabled) && isEmailServiceEnabled && !isMFADisabled {
otp := utils.GenerateOTP() otp := utils.GenerateOTP()
expires := time.Now().Add(1 * time.Minute).Unix()
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{ otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
Email: user.Email, Email: user.Email,
Otp: otp, Otp: otp,
ExpiresAt: time.Now().Add(1 * time.Minute).Unix(), ExpiresAt: expires,
}) })
if err != nil { if err != nil {
log.Debug("Failed to add otp: ", err) log.Debug("Failed to add otp: ", err)
return nil, err return nil, err
} }
mfaSession := uuid.NewString()
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expires)
if err != nil {
log.Debug("Failed to add mfasession: ", err)
return nil, err
}
cookie.SetMfaSession(gc, mfaSession)
go func() { go func() {
// exec it as go routine so that we can reduce the api latency // exec it as go routine so that we can reduce the api latency
go email.SendEmail([]string{params.Email}, constants.VerificationTypeOTP, map[string]interface{}{ go email.SendEmail([]string{params.Email}, constants.VerificationTypeOTP, map[string]interface{}{
@@ -162,7 +171,6 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
if nonce == "" { if nonce == "" {
nonce = uuid.New().String() nonce = uuid.New().String()
} }
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, code) authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, code)
if err != nil { if err != nil {
log.Debug("Failed to create auth token", err) log.Debug("Failed to create auth token", err)

View File

@@ -122,15 +122,25 @@ func MobileLoginResolver(ctx context.Context, params model.MobileLoginInput) (*m
smsBody := strings.Builder{} smsBody := strings.Builder{}
smsBody.WriteString("Your verification code is: ") smsBody.WriteString("Your verification code is: ")
smsBody.WriteString(smsCode) smsBody.WriteString(smsCode)
expires := time.Now().Add(duration).Unix()
_, err := db.Provider.UpsertOTP(ctx, &models.OTP{ _, err := db.Provider.UpsertOTP(ctx, &models.OTP{
PhoneNumber: params.PhoneNumber, PhoneNumber: params.PhoneNumber,
Otp: smsCode, Otp: smsCode,
ExpiresAt: time.Now().Add(duration).Unix(), ExpiresAt: expires,
}) })
if err != nil { if err != nil {
log.Debug("error while upserting OTP: ", err.Error()) log.Debug("error while upserting OTP: ", err.Error())
return nil, err return nil, err
} }
mfaSession := uuid.NewString()
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expires)
if err != nil {
log.Debug("Failed to add mfasession: ", err)
return nil, err
}
cookie.SetMfaSession(gc, mfaSession)
go func() { go func() {
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user) utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
smsproviders.SendSMS(params.PhoneNumber, smsBody.String()) smsproviders.SendSMS(params.PhoneNumber, smsBody.String())

View File

@@ -223,6 +223,7 @@ func MobileSignupResolver(ctx context.Context, params *model.MobileSignUpInput)
} }
go func() { go func() {
smsproviders.SendSMS(mobile, smsBody.String()) smsproviders.SendSMS(mobile, smsBody.String())
utils.RegisterEvent(ctx, constants.UserCreatedWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
}() }()
return &model.AuthResponse{ return &model.AuthResponse{
Message: "Please check the OTP in your inbox", Message: "Please check the OTP in your inbox",
@@ -298,6 +299,8 @@ func MobileSignupResolver(ctx context.Context, params *model.MobileSignUpInput)
go func() { go func() {
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user) utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
// User is also logged in with signup
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
db.Provider.AddSession(ctx, &models.Session{ db.Provider.AddSession(ctx, &models.Session{
UserID: user.ID, UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request), UserAgent: utils.GetUserAgent(gc.Request),

View File

@@ -2,6 +2,8 @@ package resolvers
import ( import (
"context" "context"
"encoding/json"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@@ -171,6 +173,17 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true) user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true)
} }
if params.AppData != nil {
appDataString := ""
appDataBytes, err := json.Marshal(params.AppData)
if err != nil {
log.Debug("failed to marshall source app_data: ", err)
return nil, errors.New("malformed app_data")
}
appDataString = string(appDataBytes)
user.AppData = &appDataString
}
user.SignupMethods = constants.AuthRecipeMethodBasicAuth user.SignupMethods = constants.AuthRecipeMethodBasicAuth
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
if err != nil { if err != nil {
@@ -301,6 +314,8 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
go func() { go func() {
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, constants.AuthRecipeMethodBasicAuth, user) utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
// User is also logged in with signup
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
db.Provider.AddSession(ctx, &models.Session{ db.Provider.AddSession(ctx, &models.Session{
UserID: user.ID, UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request), UserAgent: utils.GetUserAgent(gc.Request),

View File

@@ -2,6 +2,7 @@ package resolvers
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@@ -47,7 +48,7 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
} }
// validate if all params are not empty // 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 && params.IsMultiFactorAuthEnabled == 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 && params.AppData == nil {
log.Debug("All params are empty") log.Debug("All params are empty")
return res, fmt.Errorf("please enter at least one param to update") return res, fmt.Errorf("please enter at least one param to update")
} }
@@ -56,7 +57,6 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
log := log.WithFields(log.Fields{ log := log.WithFields(log.Fields{
"user_id": userID, "user_id": userID,
}) })
user, err := db.Provider.GetUserByID(ctx, userID) user, err := db.Provider.GetUserByID(ctx, userID)
if err != nil { if err != nil {
log.Debug("Failed to get user by id: ", err) log.Debug("Failed to get user by id: ", err)
@@ -99,7 +99,16 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
if params.Picture != nil && refs.StringValue(user.Picture) != refs.StringValue(params.Picture) { if params.Picture != nil && refs.StringValue(user.Picture) != refs.StringValue(params.Picture) {
user.Picture = params.Picture user.Picture = params.Picture
} }
if params.AppData != nil {
appDataString := ""
appDataBytes, err := json.Marshal(params.AppData)
if err != nil {
log.Debug("failed to marshall source app_data: ", err)
return nil, errors.New("malformed app_data")
}
appDataString = string(appDataBytes)
user.AppData = &appDataString
}
if params.IsMultiFactorAuthEnabled != nil && refs.BoolValue(user.IsMultiFactorAuthEnabled) != refs.BoolValue(params.IsMultiFactorAuthEnabled) { if params.IsMultiFactorAuthEnabled != nil && refs.BoolValue(user.IsMultiFactorAuthEnabled) != refs.BoolValue(params.IsMultiFactorAuthEnabled) {
if refs.BoolValue(params.IsMultiFactorAuthEnabled) { if refs.BoolValue(params.IsMultiFactorAuthEnabled) {
isEnvServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled) isEnvServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)

View File

@@ -2,6 +2,7 @@ package resolvers
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@@ -95,6 +96,17 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
user.Picture = params.Picture user.Picture = params.Picture
} }
if params.AppData != nil {
appDataString := ""
appDataBytes, err := json.Marshal(params.AppData)
if err != nil {
log.Debug("failed to marshall source app_data: ", err)
return nil, errors.New("malformed app_data")
}
appDataString = string(appDataBytes)
user.AppData = &appDataString
}
if params.IsMultiFactorAuthEnabled != nil && refs.BoolValue(user.IsMultiFactorAuthEnabled) != refs.BoolValue(params.IsMultiFactorAuthEnabled) { if params.IsMultiFactorAuthEnabled != nil && refs.BoolValue(user.IsMultiFactorAuthEnabled) != refs.BoolValue(params.IsMultiFactorAuthEnabled) {
user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled
if refs.BoolValue(params.IsMultiFactorAuthEnabled) { if refs.BoolValue(params.IsMultiFactorAuthEnabled) {

View File

@@ -125,6 +125,8 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
go func() { go func() {
if isSignUp { if isSignUp {
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, loginMethod, user) utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, loginMethod, user)
// User is also logged in with signup
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, loginMethod, user)
} else { } else {
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, loginMethod, user) utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, loginMethod, user)
} }

View File

@@ -27,6 +27,13 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod
log.Debug("Failed to get GinContext: ", err) log.Debug("Failed to get GinContext: ", err)
return res, err return res, err
} }
mfaSession, err := cookie.GetMfaSession(gc)
if err != nil {
log.Debug("Failed to get otp request by email: ", err)
return res, fmt.Errorf(`invalid session: %s`, err.Error())
}
if refs.StringValue(params.Email) == "" && refs.StringValue(params.PhoneNumber) == "" { if refs.StringValue(params.Email) == "" && refs.StringValue(params.PhoneNumber) == "" {
log.Debug("Email or phone number is required") log.Debug("Email or phone number is required")
return res, fmt.Errorf(`email or phone_number is required`) return res, fmt.Errorf(`email or phone_number is required`)
@@ -62,10 +69,15 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod
user, err = db.Provider.GetUserByPhoneNumber(ctx, refs.StringValue(params.PhoneNumber)) user, err = db.Provider.GetUserByPhoneNumber(ctx, refs.StringValue(params.PhoneNumber))
} }
if user == nil || err != nil { if user == nil || err != nil {
fmt.Println("=> failing here....", err)
log.Debug("Failed to get user by email or phone number: ", err) log.Debug("Failed to get user by email or phone number: ", err)
return res, err return res, err
} }
if _, err := memorystore.Provider.GetMfaSession(user.ID, mfaSession); err != nil {
log.Debug("Failed to get mfa session: ", err)
return res, fmt.Errorf(`invalid session: %s`, err.Error())
}
isSignUp := user.EmailVerifiedAt == nil && user.PhoneNumberVerifiedAt == nil isSignUp := user.EmailVerifiedAt == nil && user.PhoneNumberVerifiedAt == nil
// TODO - Add Login method in DB when we introduce OTP for social media login // TODO - Add Login method in DB when we introduce OTP for social media login
loginMethod := constants.AuthRecipeMethodBasicAuth loginMethod := constants.AuthRecipeMethodBasicAuth
@@ -112,6 +124,8 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod
db.Provider.DeleteOTP(gc, otp) db.Provider.DeleteOTP(gc, otp)
if isSignUp { if isSignUp {
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, loginMethod, user) utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, loginMethod, user)
// User is also logged in with signup
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, loginMethod, user)
} else { } else {
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, loginMethod, user) utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, loginMethod, user)
} }

View File

@@ -1,12 +1,18 @@
package test package test
import ( import (
"fmt"
"strings"
"testing" "testing"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/resolvers" "github.com/authorizerdev/authorizer/server/resolvers"
"github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -48,6 +54,17 @@ func mobileLoginTests(t *testing.T, s TestSetup) {
smsRequest, err := db.Provider.GetOTPByPhoneNumber(ctx, phoneNumber) smsRequest, err := db.Provider.GetOTPByPhoneNumber(ctx, phoneNumber)
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, smsRequest.Otp) assert.NotEmpty(t, smsRequest.Otp)
// Get user by phone number
user, err := db.Provider.GetUserByPhoneNumber(ctx, phoneNumber)
assert.NoError(t, err)
assert.NotNil(t, user)
// Set mfa cookie session
mfaSession := uuid.NewString()
memorystore.Provider.SetMfaSession(user.ID, mfaSession, time.Now().Add(1*time.Minute).Unix())
cookie := fmt.Sprintf("%s=%s;", constants.MfaCookieName+"_session", mfaSession)
cookie = strings.TrimSuffix(cookie, ";")
req, ctx := createContext(s)
req.Header.Set("Cookie", cookie)
verifySMSRequest, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{ verifySMSRequest, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
PhoneNumber: &phoneNumber, PhoneNumber: &phoneNumber,
Otp: smsRequest.Otp, Otp: smsRequest.Otp,

View File

@@ -1,7 +1,10 @@
package test package test
import ( import (
"fmt"
"strings"
"testing" "testing"
"time"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
@@ -9,6 +12,7 @@ import (
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/resolvers" "github.com/authorizerdev/authorizer/server/resolvers"
"github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -79,6 +83,17 @@ func mobileSingupTest(t *testing.T, s TestSetup) {
otp, err := db.Provider.GetOTPByPhoneNumber(ctx, phoneNumber) otp, err := db.Provider.GetOTPByPhoneNumber(ctx, phoneNumber)
assert.Nil(t, err) assert.Nil(t, err)
assert.NotEmpty(t, otp.Otp) assert.NotEmpty(t, otp.Otp)
// Get user by phone number
user, err := db.Provider.GetUserByPhoneNumber(ctx, phoneNumber)
assert.NoError(t, err)
assert.NotNil(t, user)
// Set mfa cookie session
mfaSession := uuid.NewString()
memorystore.Provider.SetMfaSession(user.ID, mfaSession, time.Now().Add(1*time.Minute).Unix())
cookie := fmt.Sprintf("%s=%s;", constants.MfaCookieName+"_session", mfaSession)
cookie = strings.TrimSuffix(cookie, ";")
req, ctx := createContext(s)
req.Header.Set("Cookie", cookie)
otpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{ otpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
PhoneNumber: &phoneNumber, PhoneNumber: &phoneNumber,
Otp: otp.Otp, Otp: otp.Otp,

View File

@@ -2,13 +2,18 @@ package test
import ( import (
"context" "context"
"fmt"
"strings"
"testing" "testing"
"time"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/resolvers" "github.com/authorizerdev/authorizer/server/resolvers"
"github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -89,6 +94,16 @@ func resendOTPTest(t *testing.T, s TestSetup) {
}) })
assert.Error(t, err) assert.Error(t, err)
assert.Nil(t, verifyOtpRes) assert.Nil(t, verifyOtpRes)
// Get user by email
user, err := db.Provider.GetUserByEmail(ctx, email)
assert.NoError(t, err)
assert.NotNil(t, user)
// Set mfa cookie session
mfaSession := uuid.NewString()
memorystore.Provider.SetMfaSession(user.ID, mfaSession, time.Now().Add(1*time.Minute).Unix())
cookie := fmt.Sprintf("%s=%s;", constants.MfaCookieName+"_session", mfaSession)
cookie = strings.TrimSuffix(cookie, ";")
req.Header.Set("Cookie", cookie)
verifyOtpRes, err = resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{ verifyOtpRes, err = resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
Email: &email, Email: &email,
Otp: newOtp.Otp, Otp: newOtp.Otp,

View File

@@ -2,13 +2,18 @@ package test
import ( import (
"context" "context"
"fmt"
"strings"
"testing" "testing"
"time"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/resolvers" "github.com/authorizerdev/authorizer/server/resolvers"
"github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -63,7 +68,16 @@ func verifyOTPTest(t *testing.T, s TestSetup) {
otp, err := db.Provider.GetOTPByEmail(ctx, email) otp, err := db.Provider.GetOTPByEmail(ctx, email)
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, otp.Otp) assert.NotEmpty(t, otp.Otp)
// Get user by email
user, err := db.Provider.GetUserByEmail(ctx, email)
assert.NoError(t, err)
assert.NotNil(t, user)
// Set mfa cookie session
mfaSession := uuid.NewString()
memorystore.Provider.SetMfaSession(user.ID, mfaSession, time.Now().Add(1*time.Minute).Unix())
cookie := fmt.Sprintf("%s=%s;", constants.MfaCookieName+"_session", mfaSession)
cookie = strings.TrimSuffix(cookie, ";")
req.Header.Set("Cookie", cookie)
verifyOtpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{ verifyOtpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
Email: &email, Email: &email,
Otp: otp.Otp, Otp: otp.Otp,