* integrated totp
This commit is contained in:
lemonScaletech 2023-09-06 11:26:22 +05:30
parent 9fda8c01f5
commit bbb1cf6301
17 changed files with 858 additions and 63 deletions

View File

@ -177,6 +177,7 @@ const Features = ({ variables, setVariables }: any) => {
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_PLAYGROUND}
hasReversedValue
/>
</Flex>
</Flex>

View File

@ -3,9 +3,12 @@ package crypto
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
)
// NewRSAKey to generate new RSA Key if env is not set
@ -116,3 +119,47 @@ func AsRSAStr(privateKey *rsa.PrivateKey, publickKey *rsa.PublicKey) (string, st
return privParsedPem, pubParsedPem, nil
}
func EncryptRSA(message string, key rsa.PublicKey) (string, error) {
label := []byte("OAEP Encrypted")
rng := rand.Reader
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rng, &key, []byte(message), label)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
func DecryptRSA(cipherText string, privateKey rsa.PrivateKey) (string, error) {
ct, _ := base64.StdEncoding.DecodeString(cipherText)
label := []byte("OAEP Encrypted")
rng := rand.Reader
plaintext, err := rsa.DecryptOAEP(sha256.New(), rng, &privateKey, ct, label)
if err != nil {
return "", err
}
fmt.Println("Plaintext:", string(plaintext))
return string(plaintext), nil
}
func ParseRSAPublicKey(key string) (*rsa.PublicKey, error) {
// Decode the PEM-encoded public key data.
block, _ := pem.Decode([]byte(key))
if block == nil {
return nil, fmt.Errorf("failed to parse PEM block containing public key")
}
// Parse the DER-encoded public key data.
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
// Type-assert the parsed public key to an rsa.PublicKey.
rsaPublicKey, ok := pubKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("parsed public key is not an RSA public key")
}
return rsaPublicKey, nil
}

View File

@ -3,8 +3,13 @@ package arangodb
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"image/png"
"os"
"time"
"github.com/pquerna/otp/totp"
@ -66,3 +71,35 @@ func (p *provider) ValidatePasscode(ctx context.Context, passcode string, id str
}
}
}
func (p *provider) GenerateKeysTOTP() (*rsa.PublicKey, error) {
key := os.Getenv("TOTP_PRIVATE_KEY")
var privateKey *rsa.PrivateKey
if key == "" {
privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return nil, err
}
privateKeyPEM := encodePrivateKeyToPEM(privateKey)
os.Setenv("TOTP_PRIVATE_KEY", string(privateKeyPEM))
}
publicKey := privateKey.PublicKey
return &publicKey, nil
}
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
// Marshal the private key to DER format.
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
// Create a PEM block for the private key.
privateKeyPEMBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
}
// Encode the PEM block to PEM format.
privateKeyPEM := pem.EncodeToMemory(privateKeyPEMBlock)
return privateKeyPEM
}

View File

@ -3,8 +3,13 @@ package cassandradb
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"image/png"
"os"
"time"
"github.com/pquerna/otp/totp"
@ -66,3 +71,35 @@ func (p *provider) ValidatePasscode(ctx context.Context, passcode string, id str
}
}
}
func (p *provider) GenerateKeysTOTP() (*rsa.PublicKey, error) {
key := os.Getenv("TOTP_PRIVATE_KEY")
var privateKey *rsa.PrivateKey
if key == "" {
privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return nil, err
}
privateKeyPEM := encodePrivateKeyToPEM(privateKey)
os.Setenv("TOTP_PRIVATE_KEY", string(privateKeyPEM))
}
publicKey := privateKey.PublicKey
return &publicKey, nil
}
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
// Marshal the private key to DER format.
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
// Create a PEM block for the private key.
privateKeyPEMBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
}
// Encode the PEM block to PEM format.
privateKeyPEM := pem.EncodeToMemory(privateKeyPEMBlock)
return privateKeyPEM
}

View File

@ -3,8 +3,13 @@ package couchbase
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"image/png"
"os"
"time"
"github.com/pquerna/otp/totp"
@ -66,3 +71,35 @@ func (p *provider) ValidatePasscode(ctx context.Context, passcode string, id str
}
}
}
func (p *provider) GenerateKeysTOTP() (*rsa.PublicKey, error) {
key := os.Getenv("TOTP_PRIVATE_KEY")
var privateKey *rsa.PrivateKey
if key == "" {
privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return nil, err
}
privateKeyPEM := encodePrivateKeyToPEM(privateKey)
os.Setenv("TOTP_PRIVATE_KEY", string(privateKeyPEM))
}
publicKey := privateKey.PublicKey
return &publicKey, nil
}
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
// Marshal the private key to DER format.
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
// Create a PEM block for the private key.
privateKeyPEMBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
}
// Encode the PEM block to PEM format.
privateKeyPEM := pem.EncodeToMemory(privateKeyPEMBlock)
return privateKeyPEM
}

View File

@ -3,8 +3,13 @@ package dynamodb
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"image/png"
"os"
"time"
"github.com/pquerna/otp/totp"
@ -66,3 +71,35 @@ func (p *provider) ValidatePasscode(ctx context.Context, passcode string, id str
}
}
}
func (p *provider) GenerateKeysTOTP() (*rsa.PublicKey, error) {
key := os.Getenv("TOTP_PRIVATE_KEY")
var privateKey *rsa.PrivateKey
if key == "" {
privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return nil, err
}
privateKeyPEM := encodePrivateKeyToPEM(privateKey)
os.Setenv("TOTP_PRIVATE_KEY", string(privateKeyPEM))
}
publicKey := privateKey.PublicKey
return &publicKey, nil
}
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
// Marshal the private key to DER format.
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
// Create a PEM block for the private key.
privateKeyPEMBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
}
// Encode the PEM block to PEM format.
privateKeyPEM := pem.EncodeToMemory(privateKeyPEMBlock)
return privateKeyPEM
}

View File

@ -3,8 +3,13 @@ package mongodb
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"image/png"
"os"
"time"
"github.com/pquerna/otp/totp"
@ -66,3 +71,35 @@ func (p *provider) ValidatePasscode(ctx context.Context, passcode string, id str
}
}
}
func (p *provider) GenerateKeysTOTP() (*rsa.PublicKey, error) {
key := os.Getenv("TOTP_PRIVATE_KEY")
var privateKey *rsa.PrivateKey
if key == "" {
privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return nil, err
}
privateKeyPEM := encodePrivateKeyToPEM(privateKey)
os.Setenv("TOTP_PRIVATE_KEY", string(privateKeyPEM))
}
publicKey := privateKey.PublicKey
return &publicKey, nil
}
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
// Marshal the private key to DER format.
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
// Create a PEM block for the private key.
privateKeyPEMBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
}
// Encode the PEM block to PEM format.
privateKeyPEM := pem.EncodeToMemory(privateKeyPEMBlock)
return privateKeyPEM
}

View File

@ -3,8 +3,13 @@ package provider_template
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"image/png"
"os"
"time"
"github.com/pquerna/otp/totp"
@ -66,3 +71,35 @@ func (p *provider) ValidatePasscode(ctx context.Context, passcode string, id str
}
}
}
func (p *provider) GenerateKeysTOTP() (*rsa.PublicKey, error) {
key := os.Getenv("TOTP_PRIVATE_KEY")
var privateKey *rsa.PrivateKey
if key == "" {
privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return nil, err
}
privateKeyPEM := encodePrivateKeyToPEM(privateKey)
os.Setenv("TOTP_PRIVATE_KEY", string(privateKeyPEM))
}
publicKey := privateKey.PublicKey
return &publicKey, nil
}
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
// Marshal the private key to DER format.
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
// Create a PEM block for the private key.
privateKeyPEMBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
}
// Encode the PEM block to PEM format.
privateKeyPEM := pem.EncodeToMemory(privateKeyPEMBlock)
return privateKeyPEM
}

View File

@ -3,7 +3,10 @@ package sql
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"fmt"
log "github.com/sirupsen/logrus"
"image/png"
"time"
@ -47,7 +50,7 @@ func (p *provider) GenerateTotp(ctx context.Context, id string) (*string, error)
if err != nil {
return nil, fmt.Errorf("error while updating user's totp secret")
}
log.Info("\n\n\n", &encodedText)
return &encodedText, nil
}
@ -59,10 +62,16 @@ func (p *provider) ValidatePasscode(ctx context.Context, passcode string, id str
}
// validate passcode inputted by user
for {
status := totp.Validate(passcode, *user.TotpSecret)
if status {
return status, nil
}
}
status := totp.Validate(passcode, *user.TotpSecret)
return status, nil
}
func (p *provider) GenerateKeysTOTP() (*rsa.PublicKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
return nil, err
}
publicKey := privateKey.PublicKey
return &publicKey, nil
}

24
server/env/env.go vendored
View File

@ -698,17 +698,8 @@ func InitAllEnv() error {
envData[constants.EnvKeyIsEmailServiceEnabled] = true
}
if envData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && !envData[constants.EnvKeyIsEmailServiceEnabled].(bool) && !envData[constants.EnvKeyIsSMSServiceEnabled].(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) {
envData[constants.EnvKeyDisableMagicLinkLogin] = true
envData[constants.EnvKeyDisableMailOTPLogin] = true
}
if val, ok := envData[constants.EnvKeyAllowedOrigins]; !ok || val == "" {
@ -870,21 +861,6 @@ func InitAllEnv() error {
}
}
if envData[constants.EnvKeyDisableTOTPLogin] == false && envData[constants.EnvKeyDisableMailOTPLogin].(bool) == false {
errors.New("can't enable both mfa")
}
if envData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) {
envData[constants.EnvKeyDisableTOTPLogin] = true
envData[constants.EnvKeyDisableMailOTPLogin] = true
} else {
if !envData[constants.EnvKeyDisableMailOTPLogin].(bool) && !envData[constants.EnvKeyDisableTOTPLogin].(bool) {
errors.New("can't enable both mfa methods at same time")
envData[constants.EnvKeyDisableMailOTPLogin] = false
envData[constants.EnvKeyDisableTOTPLogin] = true
}
}
err = memorystore.Provider.UpdateEnvStore(envData)
if err != nil {
log.Debug("Error while updating env store: ", err)

View File

@ -45,15 +45,18 @@ 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
ShouldShowEmailOtpScreen func(childComplexity int) int
ShouldShowMobileOtpScreen func(childComplexity int) int
TotpBase64url 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
ShouldShowEmailOtpScreen func(childComplexity int) int
ShouldShowMobileOtpScreen func(childComplexity int) int
ShouldShowMobileTotpScreen func(childComplexity int) int
ShouldShowTotpScreen func(childComplexity int) int
TokenTotp func(childComplexity int) int
TotpBase64url func(childComplexity int) int
User func(childComplexity int) int
}
EmailTemplate struct {
@ -204,6 +207,7 @@ type ComplexityRoot struct {
UpdateWebhook func(childComplexity int, params model.UpdateWebhookRequest) int
VerifyEmail func(childComplexity int, params model.VerifyEmailInput) int
VerifyOtp func(childComplexity int, params model.VerifyOTPRequest) int
VerifyTotp func(childComplexity int, params model.VerifyTOTPRequest) int
}
Pagination struct {
@ -350,6 +354,7 @@ type MutationResolver interface {
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)
VerifyTotp(ctx context.Context, params model.VerifyTOTPRequest) (*model.AuthResponse, 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)
@ -449,6 +454,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.AuthResponse.ShouldShowMobileOtpScreen(childComplexity), true
case "AuthResponse.should_show_mobile_totp_screen":
if e.complexity.AuthResponse.ShouldShowMobileTotpScreen == nil {
break
}
return e.complexity.AuthResponse.ShouldShowMobileTotpScreen(childComplexity), true
case "AuthResponse.should_show_totp_screen":
if e.complexity.AuthResponse.ShouldShowTotpScreen == nil {
break
}
return e.complexity.AuthResponse.ShouldShowTotpScreen(childComplexity), true
case "AuthResponse.tokenTOTP":
if e.complexity.AuthResponse.TokenTotp == nil {
break
}
return e.complexity.AuthResponse.TokenTotp(childComplexity), true
case "AuthResponse.totpBase64URL":
if e.complexity.AuthResponse.TotpBase64url == nil {
break
@ -1490,6 +1516,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.VerifyOtp(childComplexity, args["params"].(model.VerifyOTPRequest)), true
case "Mutation.verify_totp":
if e.complexity.Mutation.VerifyTotp == nil {
break
}
args, err := ec.field_Mutation_verify_totp_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.VerifyTotp(childComplexity, args["params"].(model.VerifyTOTPRequest)), true
case "Pagination.limit":
if e.complexity.Pagination.Limit == nil {
break
@ -2163,6 +2201,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
ec.unmarshalInputValidateSessionInput,
ec.unmarshalInputVerifyEmailInput,
ec.unmarshalInputVerifyOTPRequest,
ec.unmarshalInputVerifyTOTPRequest,
ec.unmarshalInputWebhookRequest,
)
first := true
@ -2320,12 +2359,15 @@ type AuthResponse {
message: String!
should_show_email_otp_screen: Boolean
should_show_mobile_otp_screen: Boolean
should_show_mobile_totp_screen: Boolean
should_show_totp_screen: Boolean
access_token: String
id_token: String
refresh_token: String
expires_in: Int64
user: User
totpBase64URL: String
tokenTOTP: String
}
type Response {
@ -2789,6 +2831,12 @@ input VerifyOTPRequest {
state: String
}
input VerifyTOTPRequest {
otp: String!
token: String!
state: String
}
input ResendOTPRequest {
email: String
phone_number: String
@ -2818,6 +2866,7 @@ type Mutation {
revoke(params: OAuthRevokeInput!): Response!
verify_otp(params: VerifyOTPRequest!): AuthResponse!
resend_otp(params: ResendOTPRequest!): Response!
verify_totp(params: VerifyTOTPRequest!): AuthResponse!
# admin only apis
_delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User!
@ -3298,6 +3347,21 @@ func (ec *executionContext) field_Mutation_verify_otp_args(ctx context.Context,
return args, nil
}
func (ec *executionContext) field_Mutation_verify_totp_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 model.VerifyTOTPRequest
if tmp, ok := rawArgs["params"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
arg0, err = ec.unmarshalNVerifyTOTPRequest2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerifyTOTPRequest(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{}{}
@ -3627,6 +3691,88 @@ func (ec *executionContext) fieldContext_AuthResponse_should_show_mobile_otp_scr
return fc, nil
}
func (ec *executionContext) _AuthResponse_should_show_mobile_totp_screen(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(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.ShouldShowMobileTotpScreen, 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) fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "AuthResponse",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _AuthResponse_should_show_totp_screen(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_AuthResponse_should_show_totp_screen(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.ShouldShowTotpScreen, 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) fieldContext_AuthResponse_should_show_totp_screen(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "AuthResponse",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _AuthResponse_access_token(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_AuthResponse_access_token(ctx, field)
if err != nil {
@ -3915,6 +4061,47 @@ func (ec *executionContext) fieldContext_AuthResponse_totpBase64URL(ctx context.
return fc, nil
}
func (ec *executionContext) _AuthResponse_tokenTOTP(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_AuthResponse_tokenTOTP(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.TokenTotp, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_AuthResponse_tokenTOTP(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "AuthResponse",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _EmailTemplate_id(ctx context.Context, field graphql.CollectedField, obj *model.EmailTemplate) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_EmailTemplate_id(ctx, field)
if err != nil {
@ -8049,6 +8236,10 @@ func (ec *executionContext) fieldContext_Mutation_signup(ctx context.Context, fi
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_mobile_totp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token":
@ -8061,6 +8252,8 @@ func (ec *executionContext) fieldContext_Mutation_signup(ctx context.Context, fi
return ec.fieldContext_AuthResponse_user(ctx, field)
case "totpBase64URL":
return ec.fieldContext_AuthResponse_totpBase64URL(ctx, field)
case "tokenTOTP":
return ec.fieldContext_AuthResponse_tokenTOTP(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
},
@ -8124,6 +8317,10 @@ func (ec *executionContext) fieldContext_Mutation_mobile_signup(ctx context.Cont
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_mobile_totp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token":
@ -8136,6 +8333,8 @@ func (ec *executionContext) fieldContext_Mutation_mobile_signup(ctx context.Cont
return ec.fieldContext_AuthResponse_user(ctx, field)
case "totpBase64URL":
return ec.fieldContext_AuthResponse_totpBase64URL(ctx, field)
case "tokenTOTP":
return ec.fieldContext_AuthResponse_tokenTOTP(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
},
@ -8199,6 +8398,10 @@ func (ec *executionContext) fieldContext_Mutation_login(ctx context.Context, fie
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_mobile_totp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token":
@ -8211,6 +8414,8 @@ func (ec *executionContext) fieldContext_Mutation_login(ctx context.Context, fie
return ec.fieldContext_AuthResponse_user(ctx, field)
case "totpBase64URL":
return ec.fieldContext_AuthResponse_totpBase64URL(ctx, field)
case "tokenTOTP":
return ec.fieldContext_AuthResponse_tokenTOTP(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
},
@ -8274,6 +8479,10 @@ func (ec *executionContext) fieldContext_Mutation_mobile_login(ctx context.Conte
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_mobile_totp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token":
@ -8286,6 +8495,8 @@ func (ec *executionContext) fieldContext_Mutation_mobile_login(ctx context.Conte
return ec.fieldContext_AuthResponse_user(ctx, field)
case "totpBase64URL":
return ec.fieldContext_AuthResponse_totpBase64URL(ctx, field)
case "tokenTOTP":
return ec.fieldContext_AuthResponse_tokenTOTP(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
},
@ -8515,6 +8726,10 @@ func (ec *executionContext) fieldContext_Mutation_verify_email(ctx context.Conte
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_mobile_totp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token":
@ -8527,6 +8742,8 @@ func (ec *executionContext) fieldContext_Mutation_verify_email(ctx context.Conte
return ec.fieldContext_AuthResponse_user(ctx, field)
case "totpBase64URL":
return ec.fieldContext_AuthResponse_totpBase64URL(ctx, field)
case "tokenTOTP":
return ec.fieldContext_AuthResponse_tokenTOTP(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
},
@ -8826,6 +9043,10 @@ func (ec *executionContext) fieldContext_Mutation_verify_otp(ctx context.Context
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_mobile_totp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token":
@ -8838,6 +9059,8 @@ func (ec *executionContext) fieldContext_Mutation_verify_otp(ctx context.Context
return ec.fieldContext_AuthResponse_user(ctx, field)
case "totpBase64URL":
return ec.fieldContext_AuthResponse_totpBase64URL(ctx, field)
case "tokenTOTP":
return ec.fieldContext_AuthResponse_tokenTOTP(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
},
@ -8915,6 +9138,87 @@ func (ec *executionContext) fieldContext_Mutation_resend_otp(ctx context.Context
return fc, nil
}
func (ec *executionContext) _Mutation_verify_totp(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Mutation_verify_totp(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 ec.resolvers.Mutation().VerifyTotp(rctx, fc.Args["params"].(model.VerifyTOTPRequest))
})
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) fieldContext_Mutation_verify_totp(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Mutation",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "message":
return ec.fieldContext_AuthResponse_message(ctx, field)
case "should_show_email_otp_screen":
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_mobile_totp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token":
return ec.fieldContext_AuthResponse_id_token(ctx, field)
case "refresh_token":
return ec.fieldContext_AuthResponse_refresh_token(ctx, field)
case "expires_in":
return ec.fieldContext_AuthResponse_expires_in(ctx, field)
case "user":
return ec.fieldContext_AuthResponse_user(ctx, field)
case "totpBase64URL":
return ec.fieldContext_AuthResponse_totpBase64URL(ctx, field)
case "tokenTOTP":
return ec.fieldContext_AuthResponse_tokenTOTP(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
},
}
defer func() {
if r := recover(); r != nil {
err = ec.Recover(ctx, r)
ec.Error(ctx, err)
}
}()
ctx = graphql.WithFieldContext(ctx, fc)
if fc.Args, err = ec.field_Mutation_verify_totp_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return
}
return fc, nil
}
func (ec *executionContext) _Mutation__delete_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Mutation__delete_user(ctx, field)
if err != nil {
@ -10250,6 +10554,10 @@ func (ec *executionContext) fieldContext_Query_session(ctx context.Context, fiel
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_mobile_totp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_totp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token":
@ -10262,6 +10570,8 @@ func (ec *executionContext) fieldContext_Query_session(ctx context.Context, fiel
return ec.fieldContext_AuthResponse_user(ctx, field)
case "totpBase64URL":
return ec.fieldContext_AuthResponse_totpBase64URL(ctx, field)
case "tokenTOTP":
return ec.fieldContext_AuthResponse_tokenTOTP(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
},
@ -18261,6 +18571,50 @@ func (ec *executionContext) unmarshalInputVerifyOTPRequest(ctx context.Context,
return it, nil
}
func (ec *executionContext) unmarshalInputVerifyTOTPRequest(ctx context.Context, obj interface{}) (model.VerifyTOTPRequest, error) {
var it model.VerifyTOTPRequest
asMap := map[string]interface{}{}
for k, v := range obj.(map[string]interface{}) {
asMap[k] = v
}
fieldsInOrder := [...]string{"otp", "token", "state"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
continue
}
switch k {
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
}
case "token":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("token"))
it.Token, err = ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
case "state":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
it.State, err = ec.unmarshalOString2ᚖstring(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{}{}
@ -18322,6 +18676,14 @@ func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.Selection
out.Values[i] = ec._AuthResponse_should_show_mobile_otp_screen(ctx, field, obj)
case "should_show_mobile_totp_screen":
out.Values[i] = ec._AuthResponse_should_show_mobile_totp_screen(ctx, field, obj)
case "should_show_totp_screen":
out.Values[i] = ec._AuthResponse_should_show_totp_screen(ctx, field, obj)
case "access_token":
out.Values[i] = ec._AuthResponse_access_token(ctx, field, obj)
@ -18346,6 +18708,10 @@ func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.Selection
out.Values[i] = ec._AuthResponse_totpBase64URL(ctx, field, obj)
case "tokenTOTP":
out.Values[i] = ec._AuthResponse_tokenTOTP(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@ -19152,6 +19518,15 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
return ec._Mutation_resend_otp(ctx, field)
})
if out.Values[i] == graphql.Null {
invalids++
}
case "verify_totp":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
return ec._Mutation_verify_totp(ctx, field)
})
if out.Values[i] == graphql.Null {
invalids++
}
@ -21234,6 +21609,11 @@ func (ec *executionContext) unmarshalNVerifyOTPRequest2githubᚗcomᚋauthorizer
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalNVerifyTOTPRequest2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerifyTOTPRequest(ctx context.Context, v interface{}) (model.VerifyTOTPRequest, error) {
res, err := ec.unmarshalInputVerifyTOTPRequest(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)
}

View File

@ -26,15 +26,18 @@ type AdminSignupInput struct {
}
type AuthResponse struct {
Message string `json:"message"`
ShouldShowEmailOtpScreen *bool `json:"should_show_email_otp_screen"`
ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_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"`
TotpBase64url *string `json:"totpBase64URL"`
Message string `json:"message"`
ShouldShowEmailOtpScreen *bool `json:"should_show_email_otp_screen"`
ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_otp_screen"`
ShouldShowMobileTotpScreen *bool `json:"should_show_mobile_totp_screen"`
ShouldShowTotpScreen *bool `json:"should_show_totp_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"`
TotpBase64url *string `json:"totpBase64URL"`
TokenTotp *string `json:"tokenTOTP"`
}
type DeleteEmailTemplateRequest struct {
@ -509,6 +512,12 @@ type VerifyOTPRequest struct {
State *string `json:"state"`
}
type VerifyTOTPRequest struct {
Otp string `json:"otp"`
Token string `json:"token"`
State *string `json:"state"`
}
type Webhook struct {
ID string `json:"id"`
EventName *string `json:"event_name"`

View File

@ -94,12 +94,15 @@ type AuthResponse {
message: String!
should_show_email_otp_screen: Boolean
should_show_mobile_otp_screen: Boolean
should_show_mobile_totp_screen: Boolean
should_show_totp_screen: Boolean
access_token: String
id_token: String
refresh_token: String
expires_in: Int64
user: User
totpBase64URL: String
tokenTOTP: String
}
type Response {
@ -563,6 +566,12 @@ input VerifyOTPRequest {
state: String
}
input VerifyTOTPRequest {
otp: String!
token: String!
state: String
}
input ResendOTPRequest {
email: String
phone_number: String
@ -592,6 +601,7 @@ type Mutation {
revoke(params: OAuthRevokeInput!): Response!
verify_otp(params: VerifyOTPRequest!): AuthResponse!
resend_otp(params: ResendOTPRequest!): Response!
verify_totp(params: VerifyTOTPRequest!): AuthResponse!
# admin only apis
_delete_user(params: DeleteUserInput!): Response!
_update_user(params: UpdateUserInput!): User!

View File

@ -81,6 +81,11 @@ func (r *mutationResolver) ResendOtp(ctx context.Context, params model.ResendOTP
return resolvers.ResendOTPResolver(ctx, params)
}
// VerifyTotp is the resolver for the verify_totp field.
func (r *mutationResolver) VerifyTotp(ctx context.Context, params model.VerifyTOTPRequest) (*model.AuthResponse, error) {
return resolvers.VerifyTotpResolver(ctx, params)
}
// DeleteUser is the resolver for the _delete_user field.
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
return resolvers.DeleteUserResolver(ctx, params)

View File

@ -166,10 +166,21 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
if err != nil {
log.Debug("error while generating base64 url: ", err)
}
res.TotpBase64url = base64URL
res = &model.AuthResponse{
Message: `Proceed to totp screen`,
TotpBase64url: base64URL,
TokenTotp: &user.ID,
}
return res, nil
} else {
//res.TokenTotp = &user.ID
res = &model.AuthResponse{
Message: `Proceed to totp screen`,
TokenTotp: &user.ID,
}
return res, nil
}
}
code := ""

View File

@ -253,22 +253,13 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
// 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
}
if !updatedData[constants.EnvKeyDisableMagicLinkLogin].(bool) {
updatedData[constants.EnvKeyDisableMagicLinkLogin] = true
if !updatedData[constants.EnvKeyDisableMailOTPLogin].(bool) {
updatedData[constants.EnvKeyDisableMailOTPLogin] = true
}
}
if updatedData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) {
updatedData[constants.EnvKeyDisableTOTPLogin] = true
updatedData[constants.EnvKeyDisableMailOTPLogin] = true
} else {
if !updatedData[constants.EnvKeyDisableMailOTPLogin].(bool) && !updatedData[constants.EnvKeyDisableTOTPLogin].(bool) {
errors.New("can't enable both mfa methods at same time")
if !updatedData[constants.EnvKeyDisableMagicLinkLogin].(bool) {
updatedData[constants.EnvKeyDisableMailOTPLogin] = true
updatedData[constants.EnvKeyDisableTOTPLogin] = false
}
@ -285,6 +276,21 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
}
}
if updatedData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) {
updatedData[constants.EnvKeyDisableTOTPLogin] = true
updatedData[constants.EnvKeyDisableMailOTPLogin] = true
} else {
if !updatedData[constants.EnvKeyDisableMailOTPLogin].(bool) && !updatedData[constants.EnvKeyDisableTOTPLogin].(bool) {
errors.New("can't enable both mfa methods at same time")
updatedData[constants.EnvKeyDisableMailOTPLogin] = true
updatedData[constants.EnvKeyDisableTOTPLogin] = false
} else if updatedData[constants.EnvKeyDisableMailOTPLogin].(bool) && updatedData[constants.EnvKeyDisableTOTPLogin].(bool) {
errors.New("can't disable both mfa methods at same time")
updatedData[constants.EnvKeyDisableMailOTPLogin] = true
updatedData[constants.EnvKeyDisableTOTPLogin] = false
}
}
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,

View File

@ -0,0 +1,119 @@
package resolvers
import (
"context"
"fmt"
"strings"
"time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"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/refs"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
)
// VerifyTotpResolver resolver for verify totp mutation
func VerifyTotpResolver(ctx context.Context, params model.VerifyTOTPRequest) (*model.AuthResponse, error) {
var res *model.AuthResponse
userID := params.Token
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
}
user, err := db.Provider.GetUserByID(ctx, userID)
if err != nil {
return nil, err
}
status, err := db.Provider.ValidatePasscode(ctx, params.Otp, userID)
if err != nil || !status {
return nil, fmt.Errorf("error while validating passcode", err)
}
code := ""
codeChallenge := ""
nonce := ""
roles := strings.Split(user.Roles, ",")
scope := []string{"openid", "email", "profile"}
// Get state from store
if params.State != nil {
authorizeState, _ := memorystore.Provider.GetState(refs.StringValue(params.State))
if authorizeState != "" {
authorizeStateSplit := strings.Split(authorizeState, "@@")
if len(authorizeStateSplit) > 1 {
code = authorizeStateSplit[0]
codeChallenge = authorizeStateSplit[1]
} else {
nonce = authorizeState
}
go memorystore.Provider.RemoveState(refs.StringValue(params.State))
}
}
if nonce == "" {
nonce = uuid.New().String()
}
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, code)
if err != nil {
log.Debug("Failed to create auth token", err)
return res, err
}
// TODO add to other login options as well
// Code challenge could be optional if PKCE flow is not used
if code != "" {
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
log.Debug("SetState failed: ", err)
return res, err
}
}
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
if expiresIn <= 0 {
expiresIn = 1
}
res = &model.AuthResponse{
Message: `Logged in successfully`,
AccessToken: &authToken.AccessToken.Token,
IDToken: &authToken.IDToken.Token,
ExpiresIn: &expiresIn,
User: user.AsAPIUser(),
}
cookie.SetSession(gc, authToken.FingerPrintHash)
sessionStoreKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
}
go func() {
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
db.Provider.AddSession(ctx, &models.Session{
UserID: user.ID,
UserAgent: utils.GetUserAgent(gc.Request),
IP: utils.GetIP(gc.Request),
})
}()
return res, nil
}