feat: add totp login API (#416)

* fix:
* removed hasReversedValue in playground

* feat:
* added totp methods in db's providers
* adding totp in login method

* feat:
* added toggle in dashboard
* fixing issue with env set

* feat:
* integrated totp

* feat:
* encrypted userid
* added totp_verified column in user table
* started test for totp

* feat:
* test cases totp

* test-cases:
* completed test cases
* tested for all dbs

* fixes:
* return variable to snake case
* import refactoring

* feat:
* created seperate folder for authenticator with totp subfolder
* refactored code
* created new table for authenticators
* added recovery code for totp

* feat:
* adding functions to different db providers

* feat:
* added authenticators method for all db

* feat:
* added logic for updating mfa in user_profile update

* fix:
* merge conflict

* fix:
* resolved mongodb, dynamodb and arangodb test case bug
* added new condition for checking first time totp user or not

* feat:
* changes in all respective db with authenticator

* fix:
* PR suggested changes

* fix(cassandra): list users

* Update verify otp

* fix totp login api

---------

Co-authored-by: lemonScaletech <anand.panigrahi@scaletech.xyz>
This commit is contained in:
Lakhan Samani 2023-11-16 18:30:54 +05:30 committed by GitHub
parent d8cd965004
commit fe4c693324
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 4321 additions and 1111 deletions

668
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,21 +16,23 @@
dependencies: dependencies:
"@authorizerdev/authorizer-js" "^1.2.6" "@authorizerdev/authorizer-js" "^1.2.6"
"@babel/code-frame@^7.16.7": "@babel/code-frame@^7.22.13":
version "7.16.7" version "7.22.13"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz"
integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
dependencies: dependencies:
"@babel/highlight" "^7.16.7" "@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/generator@^7.16.8": "@babel/generator@^7.23.0":
version "7.16.8" version "7.23.0"
resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz"
integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
dependencies: dependencies:
"@babel/types" "^7.16.8" "@babel/types" "^7.23.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1" jsesc "^2.5.1"
source-map "^0.5.0"
"@babel/helper-annotate-as-pure@^7.16.0": "@babel/helper-annotate-as-pure@^7.16.0":
version "7.16.7" version "7.16.7"
@ -39,35 +41,25 @@
dependencies: dependencies:
"@babel/types" "^7.16.7" "@babel/types" "^7.16.7"
"@babel/helper-environment-visitor@^7.16.7": "@babel/helper-environment-visitor@^7.22.20":
version "7.16.7" version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz" resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-function-name@^7.16.7": "@babel/helper-function-name@^7.23.0":
version "7.16.7" version "7.23.0"
resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies: dependencies:
"@babel/helper-get-function-arity" "^7.16.7" "@babel/template" "^7.22.15"
"@babel/template" "^7.16.7" "@babel/types" "^7.23.0"
"@babel/types" "^7.16.7"
"@babel/helper-get-function-arity@^7.16.7": "@babel/helper-hoist-variables@^7.22.5":
version "7.16.7" version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz" resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies: dependencies:
"@babel/types" "^7.16.7" "@babel/types" "^7.22.5"
"@babel/helper-hoist-variables@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz"
integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0": "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0":
version "7.16.7" version "7.16.7"
@ -76,31 +68,36 @@
dependencies: dependencies:
"@babel/types" "^7.16.7" "@babel/types" "^7.16.7"
"@babel/helper-split-export-declaration@^7.16.7": "@babel/helper-split-export-declaration@^7.22.6":
version "7.16.7" version "7.22.6"
resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz" resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
dependencies: dependencies:
"@babel/types" "^7.16.7" "@babel/types" "^7.22.5"
"@babel/helper-validator-identifier@^7.16.7": "@babel/helper-string-parser@^7.22.5":
version "7.16.7" version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz"
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/highlight@^7.16.7": "@babel/helper-validator-identifier@^7.22.20":
version "7.16.10" version "7.22.20"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/highlight@^7.22.13":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz"
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
dependencies: dependencies:
"@babel/helper-validator-identifier" "^7.16.7" "@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.0.0" chalk "^2.4.2"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/parser@^7.16.10", "@babel/parser@^7.16.7": "@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
version "7.16.12" version "7.23.0"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz"
integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A== integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1": "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1":
version "7.14.8" version "7.14.8"
@ -109,37 +106,38 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/template@^7.16.7": "@babel/template@^7.22.15":
version "7.16.7" version "7.22.15"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz" resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies: dependencies:
"@babel/code-frame" "^7.16.7" "@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.16.7" "@babel/parser" "^7.22.15"
"@babel/types" "^7.16.7" "@babel/types" "^7.22.15"
"@babel/traverse@^7.4.5": "@babel/traverse@^7.4.5":
version "7.16.10" version "7.23.2"
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz"
integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
dependencies: dependencies:
"@babel/code-frame" "^7.16.7" "@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.16.8" "@babel/generator" "^7.23.0"
"@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.16.7" "@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.16.10" "@babel/parser" "^7.23.0"
"@babel/types" "^7.16.8" "@babel/types" "^7.23.0"
debug "^4.1.0" debug "^4.1.0"
globals "^11.1.0" globals "^11.1.0"
"@babel/types@^7.16.7", "@babel/types@^7.16.8": "@babel/types@^7.16.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
version "7.16.8" version "7.23.0"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz" resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz"
integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies: dependencies:
"@babel/helper-validator-identifier" "^7.16.7" "@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@emotion/is-prop-valid@^0.8.8": "@emotion/is-prop-valid@^0.8.8":
@ -164,6 +162,38 @@
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@jridgewell/gen-mapping@^0.3.2":
version "0.3.3"
resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz"
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
dependencies:
"@jridgewell/set-array" "^1.0.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.1"
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz"
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
version "1.4.15"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.20"
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz"
integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@types/history@*": "@types/history@*":
version "4.7.9" version "4.7.9"
resolved "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz" resolved "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz"
@ -256,7 +286,7 @@ camelize@^1.0.0:
resolved "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz" resolved "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
chalk@^2.0.0: chalk@^2.4.2:
version "2.4.2" version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@ -275,7 +305,7 @@ color-convert@^1.9.0:
color-name@1.1.3: color-name@1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
cross-fetch@^3.1.5: cross-fetch@^3.1.5:
version "3.1.8" version "3.1.8"
@ -318,7 +348,7 @@ esbuild@^0.12.17:
escape-string-regexp@^1.0.5: escape-string-regexp@^1.0.5:
version "1.0.5" version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
globals@^11.1.0: globals@^11.1.0:
version "11.12.0" version "11.12.0"
@ -516,11 +546,6 @@ shallowequal@^1.1.0:
resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz" resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
source-map@^0.5.0:
version "0.5.7"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
styled-components@^5.3.0, "styled-components@>= 2": styled-components@^5.3.0, "styled-components@>= 2":
version "5.3.3" version "5.3.3"
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz" resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz"

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,8 @@ const Features = ({ variables, setVariables }: any) => {
/> />
</Flex> </Flex>
</Flex> </Flex>
<Flex> <Flex>
<Flex w="100%" justifyContent="start" alignItems="center"> <Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Email Verification:</Text> <Text fontSize="sm">Email Verification:</Text>
@ -97,6 +99,7 @@ const Features = ({ variables, setVariables }: any) => {
also ignore the user MFA setting. also ignore the user MFA setting.
</Text> </Text>
</Flex> </Flex>
<Flex justifyContent="start" mb={3}> <Flex justifyContent="start" mb={3}>
<InputField <InputField
variables={variables} variables={variables}
@ -106,6 +109,46 @@ const Features = ({ variables, setVariables }: any) => {
/> />
</Flex> </Flex>
</Flex> </Flex>
{
!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION &&
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">TOTP:</Text>
<Text fontSize="x-small">
Note: to enable totp mfa
</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_TOTP_LOGIN}
hasReversedValue
/>
</Flex>
</Flex>
}
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION &&
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">EMAIL OTP:</Text>
<Text fontSize="x-small">
Note: to enable email otp mfa
</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MAIL_OTP_LOGIN}
hasReversedValue
/>
</Flex>
</Flex>}
<Flex alignItems="center"> <Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column"> <Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm"> <Text fontSize="sm">

View File

@ -85,6 +85,8 @@ export const SwitchInputType = {
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION', DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION', ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
DISABLE_PLAYGROUND: 'DISABLE_PLAYGROUND', DISABLE_PLAYGROUND: 'DISABLE_PLAYGROUND',
DISABLE_TOTP_LOGIN: 'DISABLE_TOTP_LOGIN',
DISABLE_MAIL_OTP_LOGIN: 'DISABLE_MAIL_OTP_LOGIN',
}; };
export const DateInputType = { export const DateInputType = {
@ -169,6 +171,8 @@ export interface envVarTypes {
DEFAULT_AUTHORIZE_RESPONSE_TYPE: string; DEFAULT_AUTHORIZE_RESPONSE_TYPE: string;
DEFAULT_AUTHORIZE_RESPONSE_MODE: string; DEFAULT_AUTHORIZE_RESPONSE_MODE: string;
DISABLE_PLAYGROUND: boolean; DISABLE_PLAYGROUND: boolean;
DISABLE_TOTP_LOGIN: boolean;
DISABLE_MAIL_OTP_LOGIN: boolean;
} }
export const envSubViews = { export const envSubViews = {

View File

@ -74,6 +74,8 @@ export const EnvVariablesQuery = `
DEFAULT_AUTHORIZE_RESPONSE_TYPE DEFAULT_AUTHORIZE_RESPONSE_TYPE
DEFAULT_AUTHORIZE_RESPONSE_MODE DEFAULT_AUTHORIZE_RESPONSE_MODE
DISABLE_PLAYGROUND DISABLE_PLAYGROUND
DISABLE_TOTP_LOGIN
DISABLE_MAIL_OTP_LOGIN
} }
} }
`; `;

View File

@ -94,6 +94,8 @@ const Environment = () => {
DEFAULT_AUTHORIZE_RESPONSE_TYPE: '', DEFAULT_AUTHORIZE_RESPONSE_TYPE: '',
DEFAULT_AUTHORIZE_RESPONSE_MODE: '', DEFAULT_AUTHORIZE_RESPONSE_MODE: '',
DISABLE_PLAYGROUND: false, DISABLE_PLAYGROUND: false,
DISABLE_TOTP_LOGIN: false,
DISABLE_MAIL_OTP_LOGIN: true,
}); });
const [fieldVisibility, setFieldVisibility] = React.useState< const [fieldVisibility, setFieldVisibility] = React.useState<

View File

@ -10,157 +10,155 @@
"@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/trace-mapping" "^0.3.9"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13":
version "7.21.4" version "7.22.13"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz"
integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
dependencies: dependencies:
"@babel/highlight" "^7.18.6" "@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/compat-data@^7.21.4": "@babel/compat-data@^7.22.9":
version "7.21.4" version "7.23.3"
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz"
integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== integrity sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0": "@babel/core@^7.0.0", "@babel/core@^7.0.0-0":
version "7.21.4" version "7.23.3"
resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz" resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz"
integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==
dependencies: dependencies:
"@ampproject/remapping" "^2.2.0" "@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.21.4" "@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.21.4" "@babel/generator" "^7.23.3"
"@babel/helper-compilation-targets" "^7.21.4" "@babel/helper-compilation-targets" "^7.22.15"
"@babel/helper-module-transforms" "^7.21.2" "@babel/helper-module-transforms" "^7.23.3"
"@babel/helpers" "^7.21.0" "@babel/helpers" "^7.23.2"
"@babel/parser" "^7.21.4" "@babel/parser" "^7.23.3"
"@babel/template" "^7.20.7" "@babel/template" "^7.22.15"
"@babel/traverse" "^7.21.4" "@babel/traverse" "^7.23.3"
"@babel/types" "^7.21.4" "@babel/types" "^7.23.3"
convert-source-map "^1.7.0" convert-source-map "^2.0.0"
debug "^4.1.0" debug "^4.1.0"
gensync "^1.0.0-beta.2" gensync "^1.0.0-beta.2"
json5 "^2.2.2" json5 "^2.2.3"
semver "^6.3.0" semver "^6.3.1"
"@babel/generator@^7.21.4": "@babel/generator@^7.23.3":
version "7.21.4" version "7.23.3"
resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz"
integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==
dependencies: dependencies:
"@babel/types" "^7.21.4" "@babel/types" "^7.23.3"
"@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17" "@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1" jsesc "^2.5.1"
"@babel/helper-compilation-targets@^7.21.4": "@babel/helper-compilation-targets@^7.22.15":
version "7.21.4" version "7.22.15"
resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz"
integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==
dependencies: dependencies:
"@babel/compat-data" "^7.21.4" "@babel/compat-data" "^7.22.9"
"@babel/helper-validator-option" "^7.21.0" "@babel/helper-validator-option" "^7.22.15"
browserslist "^4.21.3" browserslist "^4.21.9"
lru-cache "^5.1.1" lru-cache "^5.1.1"
semver "^6.3.0" semver "^6.3.1"
"@babel/helper-environment-visitor@^7.18.9": "@babel/helper-environment-visitor@^7.22.20":
version "7.18.9" version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-function-name@^7.21.0": "@babel/helper-function-name@^7.23.0":
version "7.21.0" version "7.23.0"
resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies: dependencies:
"@babel/template" "^7.20.7" "@babel/template" "^7.22.15"
"@babel/types" "^7.21.0" "@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.18.6": "@babel/helper-hoist-variables@^7.22.5":
version "7.18.6" version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies: dependencies:
"@babel/types" "^7.18.6" "@babel/types" "^7.22.5"
"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6": "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.22.15":
version "7.18.6" version "7.22.15"
resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz" resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz"
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==
dependencies: dependencies:
"@babel/types" "^7.18.6" "@babel/types" "^7.22.15"
"@babel/helper-module-transforms@^7.21.2": "@babel/helper-module-transforms@^7.23.3":
version "7.21.2" version "7.23.3"
resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz"
integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==
dependencies: dependencies:
"@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-module-imports" "^7.18.6" "@babel/helper-module-imports" "^7.22.15"
"@babel/helper-simple-access" "^7.20.2" "@babel/helper-simple-access" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-split-export-declaration" "^7.22.6"
"@babel/helper-validator-identifier" "^7.19.1" "@babel/helper-validator-identifier" "^7.22.20"
"@babel/template" "^7.20.7"
"@babel/traverse" "^7.21.2"
"@babel/types" "^7.21.2"
"@babel/helper-plugin-utils@^7.16.5": "@babel/helper-plugin-utils@^7.16.5":
version "7.16.5" version "7.16.5"
resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz" resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz"
integrity sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ== integrity sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ==
"@babel/helper-simple-access@^7.20.2": "@babel/helper-simple-access@^7.22.5":
version "7.20.2" version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz"
integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==
dependencies: dependencies:
"@babel/types" "^7.20.2" "@babel/types" "^7.22.5"
"@babel/helper-split-export-declaration@^7.18.6": "@babel/helper-split-export-declaration@^7.22.6":
version "7.18.6" version "7.22.6"
resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
dependencies: dependencies:
"@babel/types" "^7.18.6" "@babel/types" "^7.22.5"
"@babel/helper-string-parser@^7.19.4": "@babel/helper-string-parser@^7.22.5":
version "7.19.4" version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz"
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": "@babel/helper-validator-identifier@^7.22.20":
version "7.19.1" version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-option@^7.21.0": "@babel/helper-validator-option@^7.22.15":
version "7.21.0" version "7.22.15"
resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz"
integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==
"@babel/helpers@^7.21.0": "@babel/helpers@^7.23.2":
version "7.21.0" version "7.23.2"
resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz"
integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==
dependencies: dependencies:
"@babel/template" "^7.20.7" "@babel/template" "^7.22.15"
"@babel/traverse" "^7.21.0" "@babel/traverse" "^7.23.2"
"@babel/types" "^7.21.0" "@babel/types" "^7.23.0"
"@babel/highlight@^7.18.6": "@babel/highlight@^7.22.13":
version "7.18.6" version "7.22.20"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz"
integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
dependencies: dependencies:
"@babel/helper-validator-identifier" "^7.18.6" "@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.0.0" chalk "^2.4.2"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/parser@^7.20.7", "@babel/parser@^7.21.4": "@babel/parser@^7.22.15", "@babel/parser@^7.23.3":
version "7.21.4" version "7.23.3"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz"
integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==
"@babel/plugin-syntax-jsx@^7.12.13": "@babel/plugin-syntax-jsx@^7.12.13":
version "7.16.5" version "7.16.5"
@ -176,38 +174,38 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/template@^7.20.7": "@babel/template@^7.22.15":
version "7.20.7" version "7.22.15"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies: dependencies:
"@babel/code-frame" "^7.18.6" "@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.20.7" "@babel/parser" "^7.22.15"
"@babel/types" "^7.20.7" "@babel/types" "^7.22.15"
"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4": "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3":
version "7.21.4" version "7.23.3"
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz"
integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==
dependencies: dependencies:
"@babel/code-frame" "^7.21.4" "@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.21.4" "@babel/generator" "^7.23.3"
"@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.21.0" "@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.21.4" "@babel/parser" "^7.23.3"
"@babel/types" "^7.21.4" "@babel/types" "^7.23.3"
debug "^4.1.0" debug "^4.1.0"
globals "^11.1.0" globals "^11.1.0"
"@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4": "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3":
version "7.21.4" version "7.23.3"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz" resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz"
integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==
dependencies: dependencies:
"@babel/helper-string-parser" "^7.19.4" "@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.19.1" "@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@chakra-ui/accordion@1.4.2": "@chakra-ui/accordion@1.4.2":
@ -896,33 +894,28 @@
"@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@3.1.0": "@jridgewell/resolve-uri@^3.1.0":
version "3.1.0" version "3.1.1"
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/set-array@^1.0.1": "@jridgewell/set-array@^1.0.1":
version "1.1.2" version "1.1.2"
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/sourcemap-codec@^1.4.10": "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
version "1.4.15" version "1.4.15"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/sourcemap-codec@1.4.14":
version "1.4.14"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.18" version "0.3.20"
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz"
integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
dependencies: dependencies:
"@jridgewell/resolve-uri" "3.1.0" "@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14" "@jridgewell/sourcemap-codec" "^1.4.14"
"@popperjs/core@^2.9.3": "@popperjs/core@^2.9.3":
version "2.11.0" version "2.11.0"
@ -1074,27 +1067,27 @@ babel-plugin-macros@^2.6.1:
cosmiconfig "^6.0.0" cosmiconfig "^6.0.0"
resolve "^1.12.0" resolve "^1.12.0"
browserslist@^4.21.3, "browserslist@>= 4.21.0": browserslist@^4.21.9, "browserslist@>= 4.21.0":
version "4.21.5" version "4.22.1"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz"
integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==
dependencies: dependencies:
caniuse-lite "^1.0.30001449" caniuse-lite "^1.0.30001541"
electron-to-chromium "^1.4.284" electron-to-chromium "^1.4.535"
node-releases "^2.0.8" node-releases "^2.0.13"
update-browserslist-db "^1.0.10" update-browserslist-db "^1.0.13"
callsites@^3.0.0: callsites@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
caniuse-lite@^1.0.30001449: caniuse-lite@^1.0.30001541:
version "1.0.30001480" version "1.0.30001561"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz"
integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ== integrity sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==
chalk@^2.0.0: chalk@^2.4.2:
version "2.4.2" version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@ -1125,13 +1118,18 @@ compute-scroll-into-view@1.0.14:
resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz" resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz"
integrity sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ== integrity sha512-mKDjINe3tc6hGelUMNDzuhorIUZ7kS7BwyY0r2wQd2HOH2tRuJykiC06iSEX8y1TuhNzvz4GcJnK16mM2J1NMQ==
convert-source-map@^1.5.0, convert-source-map@^1.7.0: convert-source-map@^1.5.0:
version "1.8.0" version "1.8.0"
resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz"
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
dependencies: dependencies:
safe-buffer "~5.1.1" safe-buffer "~5.1.1"
convert-source-map@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz"
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
copy-to-clipboard@3.3.1: copy-to-clipboard@3.3.1:
version "3.3.1" version "3.3.1"
resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz" resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz"
@ -1140,9 +1138,9 @@ copy-to-clipboard@3.3.1:
toggle-selection "^1.0.6" toggle-selection "^1.0.6"
core-js@^3.6.4: core-js@^3.6.4:
version "3.30.1" version "3.33.2"
resolved "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz" resolved "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz"
integrity sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ== integrity sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==
cosmiconfig@^6.0.0: cosmiconfig@^6.0.0:
version "6.0.0" version "6.0.0"
@ -1156,11 +1154,11 @@ cosmiconfig@^6.0.0:
yaml "^1.7.2" yaml "^1.7.2"
cross-fetch@^3.0.4: cross-fetch@^3.0.4:
version "3.1.5" version "3.1.8"
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==
dependencies: dependencies:
node-fetch "2.6.7" node-fetch "^2.6.12"
css-box-model@1.2.1: css-box-model@1.2.1:
version "1.2.1" version "1.2.1"
@ -1210,10 +1208,10 @@ draftjs-utils@^0.10.2:
resolved "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz" resolved "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz"
integrity sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg== integrity sha512-EstHqr3R3JVcilJrBaO/A+01GvwwKmC7e4TCjC7S94ZeMh4IVmf60OuQXtHHpwItK8C2JCi3iljgN5KHkJboUg==
electron-to-chromium@^1.4.284: electron-to-chromium@^1.4.535:
version "1.4.365" version "1.4.581"
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.365.tgz" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.581.tgz"
integrity sha512-FRHZO+1tUNO4TOPXmlxetkoaIY8uwHzd1kKopK/Gx2SKn1L47wJXWD44wxP5CGRyyP98z/c8e1eBzJrgPeiBOg== integrity sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==
error-ex@^1.3.1: error-ex@^1.3.1:
version "1.3.2" version "1.3.2"
@ -1405,9 +1403,9 @@ immutable@~3.7.4:
integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==
"immutable@3.x.x || 4.x.x": "immutable@3.x.x || 4.x.x":
version "4.3.0" version "4.3.4"
resolved "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz" resolved "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz"
integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==
import-fresh@^3.1.0: import-fresh@^3.1.0:
version "3.3.0" version "3.3.0"
@ -1451,7 +1449,7 @@ json-parse-even-better-errors@^2.3.0:
resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
json5@^2.2.2: json5@^2.2.3:
version "2.2.3" version "2.2.3"
resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
@ -1497,17 +1495,17 @@ ms@2.1.2:
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-fetch@2.6.7: node-fetch@^2.6.12:
version "2.6.7" version "2.7.0"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies: dependencies:
whatwg-url "^5.0.0" whatwg-url "^5.0.0"
node-releases@^2.0.8: node-releases@^2.0.13:
version "2.0.10" version "2.0.13"
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz"
integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==
object-assign@^4.1.0, object-assign@^4.1.1: object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1" version "4.1.1"
@ -1739,7 +1737,7 @@ scheduler@^0.20.2:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" object-assign "^4.1.1"
semver@^6.3.0: semver@^6.3.1:
version "6.3.1" version "6.3.1"
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
@ -1815,19 +1813,19 @@ typescript@^4.5.4:
integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==
ua-parser-js@^0.7.18: ua-parser-js@^0.7.18:
version "0.7.35" version "0.7.37"
resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz" resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.37.tgz"
integrity sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g== integrity sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==
uc.micro@^1.0.1: uc.micro@^1.0.1:
version "1.0.6" version "1.0.6"
resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz" resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
update-browserslist-db@^1.0.10: update-browserslist-db@^1.0.13:
version "1.0.11" version "1.0.13"
resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz" resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz"
integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
dependencies: dependencies:
escalade "^3.1.1" escalade "^3.1.1"
picocolors "^1.0.0" picocolors "^1.0.0"

View File

@ -0,0 +1,23 @@
package providers
import "context"
// AuthenticatorConfig defines authenticator config
type AuthenticatorConfig struct {
// ScannerImage is the base64 of QR code image
ScannerImage string
// Secrets is the secret key
Secret string
// RecoveryCode is the secret key
RecoveryCodes []string
}
// Provider defines authenticators provider
type Provider interface {
// Generate totp: to generate totp, store secret into db and returns base64 of QR code image
Generate(ctx context.Context, id string) (*AuthenticatorConfig, error)
// Validate totp: user passcode with secret stored in our db
Validate(ctx context.Context, passcode string, id string) (bool, error)
// RecoveryCode totp: gives a recovery code for first time user
RecoveryCode(ctx context.Context, id string) (*string, error)
}

View File

@ -0,0 +1,23 @@
package totp
import (
"context"
)
type provider struct {
ctx context.Context
}
// TOTPConfig defines totp config
type TOTPConfig struct {
ScannerImage string
Secret string
}
// NewProvider returns a new totp provider
func NewProvider() (*provider, error) {
ctx := context.Background()
return &provider{
ctx: ctx,
}, nil
}

View File

@ -0,0 +1,128 @@
package totp
import (
"bytes"
"context"
"encoding/json"
"fmt"
"image/png"
"time"
"github.com/google/uuid"
"github.com/pquerna/otp/totp"
"github.com/authorizerdev/authorizer/server/authenticators/providers"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/refs"
)
// Generate generates a Time-Based One-Time Password (TOTP) for a user and returns the base64-encoded QR code for frontend display.
func (p *provider) Generate(ctx context.Context, id string) (*providers.AuthenticatorConfig, error) {
var buf bytes.Buffer
//get user details
user, err := db.Provider.GetUserByID(ctx, id)
if err != nil {
return nil, fmt.Errorf("error while getting user details")
}
// generate totp, Authenticators hash is valid for 30 seconds
key, err := totp.Generate(totp.GenerateOpts{
Issuer: "authorizer",
AccountName: refs.StringValue(user.Email),
})
if err != nil {
return nil, fmt.Errorf("error while genrating totp")
}
//generating image for key and encoding to base64 for displaying in frontend
img, err := key.Image(200, 200)
if err != nil {
return nil, fmt.Errorf("error while creating qr image for totp")
}
png.Encode(&buf, img)
encodedText := crypto.EncryptB64(buf.String())
secret := key.Secret()
recoveryCodes := []string{}
for i := 0; i < 10; i++ {
recoveryCodes = append(recoveryCodes, uuid.NewString())
}
// Converting recoveryCodes to string
recoverCodesMap := map[string]bool{}
for i := 0; i < len(recoveryCodes); i++ {
recoverCodesMap[recoveryCodes[i]] = false
}
// Converting recoveryCodesMap to string
jsonData, err := json.Marshal(recoverCodesMap)
if err != nil {
return nil, fmt.Errorf("error while converting recoveryCodes to string")
}
recoveryCodesString := string(jsonData)
totpModel := &models.Authenticator{
Secret: secret,
RecoveryCodes: refs.NewStringRef(recoveryCodesString),
UserID: user.ID,
Method: constants.EnvKeyTOTPAuthenticator,
}
_, err = db.Provider.AddAuthenticator(ctx, totpModel)
if err != nil {
return nil, fmt.Errorf("error while inserting into totp table")
}
return &providers.AuthenticatorConfig{
ScannerImage: encodedText,
Secret: secret,
RecoveryCodes: recoveryCodes,
}, nil
}
// Validate validates a Time-Based One-Time Password (TOTP) against the stored TOTP secret for a user.
func (p *provider) Validate(ctx context.Context, passcode string, userID string) (bool, error) {
// get totp details
totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, userID, constants.EnvKeyTOTPAuthenticator)
if err != nil {
return false, fmt.Errorf("error while getting totp details from authenticators")
}
status := totp.Validate(passcode, totpModel.Secret)
// checks if user not signed in for totp and totp code is correct then VerifiedAt will be stored in db
if totpModel.VerifiedAt == nil {
if status {
timeNow := time.Now().Unix()
totpModel.VerifiedAt = &timeNow
_, err = db.Provider.UpdateAuthenticator(ctx, totpModel)
if err != nil {
return false, fmt.Errorf("error while updaing authenticator table for totp")
}
return status, nil
}
return status, nil
}
return status, nil
}
// RecoveryCode generates a recovery code for a user's TOTP authentication, if not already verified.
func (p *provider) RecoveryCode(ctx context.Context, id string) (*string, error) {
// get totp details
// totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, id, constants.EnvKeyTOTPAuthenticator)
// if err != nil {
// return nil, fmt.Errorf("error while getting totp details from authenticators")
// }
// //TODO *totpModel.RecoveryCode == "null" used to just verify couchbase recoveryCode value to be nil
// // have to find another way round
// if totpModel.RecoveryCode == nil || *totpModel.RecoveryCode == "null" {
// recoveryCode := utils.GenerateTOTPRecoveryCode()
// totpModel.RecoveryCode = &recoveryCode
// _, err = db.Provider.UpdateAuthenticator(ctx, totpModel)
// if err != nil {
// return nil, fmt.Errorf("error while updaing authenticator table for totp")
// }
// return &recoveryCode, nil
// }
return nil, nil
}

View File

@ -0,0 +1,26 @@
package authenticators
import (
"github.com/authorizerdev/authorizer/server/authenticators/providers"
"github.com/authorizerdev/authorizer/server/authenticators/providers/totp"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/memorystore"
)
// Provider is the global authenticators provider.
var Provider providers.Provider
// InitTOTPStore initializes the TOTP authenticator store if it's not disabled in the environment variables.
// It sets the global Provider variable to a new TOTP provider.
func InitTOTPStore() error {
var err error
isTOTPEnvServiceDisabled, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
if !isTOTPEnvServiceDisabled {
Provider, err = totp.NewProvider()
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,7 @@
package constants
// Authenticators Methods
const (
// EnvKeyTOTPAuthenticator key for env variable TOTP
EnvKeyTOTPAuthenticator = "totp"
)

View File

@ -160,6 +160,12 @@ const (
// EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION // EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION
// this variable is used to completely disable multi factor authentication. It will have no effect on profile preference // this variable is used to completely disable multi factor authentication. It will have no effect on profile preference
EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION" EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION"
// EnvKeyDisableTOTPLogin is key for env variable DISABLE_TOTP_LOGIN
// this variable is used to completely disable totp verification
EnvKeyDisableTOTPLogin = "DISABLE_TOTP_LOGIN"
// EnvKeyDisableMailOTPLogin is key for env variable DISABLE_MAIL_OTP_LOGIN
// this variable is used to completely disable totp verification
EnvKeyDisableMailOTPLogin = "DISABLE_MAIL_OTP_LOGIN"
// EnvKeyDisablePhoneVerification is key for env variable DISABLE_PHONE_VERIFICATION // EnvKeyDisablePhoneVerification is key for env variable DISABLE_PHONE_VERIFICATION
// this variable is used to disable phone verification // this variable is used to disable phone verification
EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION" EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION"

View File

@ -3,7 +3,9 @@ package crypto
import ( import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/base64"
"encoding/pem" "encoding/pem"
"errors" "errors"
) )
@ -116,3 +118,24 @@ func AsRSAStr(privateKey *rsa.PrivateKey, publickKey *rsa.PublicKey) (string, st
return privParsedPem, pubParsedPem, nil 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
}
return string(plaintext), nil
}

View File

@ -37,7 +37,6 @@ func InitDB() error {
return err return err
} }
} }
if isArangoDB { if isArangoDB {
log.Info("Initializing ArangoDB Driver") log.Info("Initializing ArangoDB Driver")
Provider, err = arangodb.NewProvider() Provider, err = arangodb.NewProvider()

View File

@ -0,0 +1,16 @@
package models
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
// Authenticators model for db
type Authenticator struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"`
Method string `json:"method" bson:"method" cql:"method" dynamo:"method"`
Secret string `json:"secret" bson:"secret" cql:"secret" dynamo:"secret"`
RecoveryCodes *string `json:"recovery_codes" bson:"recovery_codes" cql:"recovery_codes" dynamo:"recovery_codes"`
VerifiedAt *int64 `json:"verified_at" bson:"verified_at" cql:"verified_at" dynamo:"verified_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}

View File

@ -1,6 +1,6 @@
package models package models
// Collections / Tables available for authorizer in the database // CollectionList / Tables available for authorizer in the database
type CollectionList struct { type CollectionList struct {
User string User string
VerificationRequest string VerificationRequest string
@ -11,6 +11,7 @@ type CollectionList struct {
EmailTemplate string EmailTemplate string
OTP string OTP string
SMSVerificationRequest string SMSVerificationRequest string
Authenticators string
} }
var ( var (
@ -27,5 +28,6 @@ var (
EmailTemplate: Prefix + "email_templates", EmailTemplate: Prefix + "email_templates",
OTP: Prefix + "otps", OTP: Prefix + "otps",
SMSVerificationRequest: Prefix + "sms_verification_requests", SMSVerificationRequest: Prefix + "sms_verification_requests",
Authenticators: Prefix + "authenticators",
} }
) )

View File

@ -0,0 +1,78 @@
package arangodb
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
arangoDriver "github.com/arangodb/go-driver"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.Key = authenticators.ID
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
authenticatorsCollection, _ := p.db.Collection(ctx, models.Collections.Authenticators)
meta, err := authenticatorsCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), authenticators)
if err != nil {
return authenticators, err
}
authenticators.Key = meta.Key
authenticators.ID = meta.ID.String()
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
authenticators.UpdatedAt = time.Now().Unix()
collection, _ := p.db.Collection(ctx, models.Collections.Authenticators)
meta, err := collection.UpdateDocument(ctx, authenticators.Key, authenticators)
if err != nil {
return authenticators, err
}
authenticators.Key = meta.Key
authenticators.ID = meta.ID.String()
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators *models.Authenticator
query := fmt.Sprintf("FOR d in %s FILTER d.user_id == @user_id AND d.method == @method LIMIT 1 RETURN d", models.Collections.Authenticators)
bindVars := map[string]interface{}{
"user_id": userId,
"method": authenticatorType,
}
cursor, err := p.db.Query(ctx, query, bindVars)
if err != nil {
return authenticators, err
}
defer cursor.Close()
for {
if !cursor.HasMore() {
if authenticators == nil {
return authenticators, fmt.Errorf("authenticator not found")
}
break
}
_, err := cursor.ReadDocument(ctx, &authenticators)
if err != nil {
return authenticators, err
}
}
return authenticators, nil
}

View File

@ -186,6 +186,7 @@ func NewProvider() (*provider, error) {
webhookLogCollection.EnsureHashIndex(ctx, []string{"webhook_id"}, &arangoDriver.EnsureHashIndexOptions{ webhookLogCollection.EnsureHashIndex(ctx, []string{"webhook_id"}, &arangoDriver.EnsureHashIndexOptions{
Sparse: true, Sparse: true,
}) })
emailTemplateCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.EmailTemplate) emailTemplateCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.EmailTemplate)
if err != nil { if err != nil {
return nil, err return nil, err
@ -204,6 +205,7 @@ func NewProvider() (*provider, error) {
Unique: true, Unique: true,
Sparse: true, Sparse: true,
}) })
otpCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.OTP) otpCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.OTP)
if err != nil { if err != nil {
return nil, err return nil, err
@ -222,6 +224,26 @@ func NewProvider() (*provider, error) {
Unique: true, Unique: true,
Sparse: true, Sparse: true,
}) })
//authenticators table define
authenticatorsCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Authenticators)
if err != nil {
return nil, err
}
if !authenticatorsCollectionExists {
_, err = arangodb.CreateCollection(ctx, models.Collections.Authenticators, nil)
if err != nil {
return nil, err
}
}
authenticatorsCollection, err := arangodb.Collection(ctx, models.Collections.Authenticators)
if err != nil {
return nil, err
}
authenticatorsCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{
Sparse: true,
})
return &provider{ return &provider{
db: arangodb, db: arangodb,
}, err }, err

View File

@ -0,0 +1,133 @@
package cassandradb
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strings"
"time"
"github.com/gocql/gocql"
"github.com/google/uuid"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
bytes, err := json.Marshal(authenticators)
if err != nil {
return authenticators, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
authenticatorsMap := map[string]interface{}{}
err = decoder.Decode(&authenticatorsMap)
if err != nil {
return authenticators, err
}
fields := "("
values := "("
for key, value := range authenticatorsMap {
if value != nil {
if key == "_id" {
fields += "id,"
} else {
fields += key + ","
}
valueType := reflect.TypeOf(value)
if valueType.Name() == "string" {
values += fmt.Sprintf("'%s',", value.(string))
} else {
values += fmt.Sprintf("%v,", value)
}
}
}
fields = fields[:len(fields)-1] + ")"
values = values[:len(values)-1] + ")"
query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.Authenticators, fields, values)
err = p.db.Query(query).Exec()
if err != nil {
return authenticators, err
}
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
authenticators.UpdatedAt = time.Now().Unix()
bytes, err := json.Marshal(authenticators)
if err != nil {
return authenticators, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
authenticatorsMap := map[string]interface{}{}
err = decoder.Decode(&authenticatorsMap)
if err != nil {
return authenticators, err
}
updateFields := ""
for key, value := range authenticatorsMap {
if key == "_id" {
continue
}
if key == "_key" {
continue
}
if value == nil {
updateFields += fmt.Sprintf("%s = null, ", key)
continue
}
valueType := reflect.TypeOf(value)
if valueType.Name() == "string" {
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
} else {
updateFields += fmt.Sprintf("%s = %v, ", key, value)
}
}
updateFields = strings.Trim(updateFields, " ")
updateFields = strings.TrimSuffix(updateFields, ",")
query := fmt.Sprintf("UPDATE %s SET %s WHERE id = '%s'", KeySpace+"."+models.Collections.Authenticators, updateFields, authenticators.ID)
err = p.db.Query(query).Exec()
if err != nil {
return authenticators, err
}
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators models.Authenticator
query := fmt.Sprintf("SELECT id, user_id, method, secret, recovery_codes, verified_at, created_at, updated_at FROM %s WHERE user_id = '%s' AND method = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.Authenticators, userId, authenticatorType)
err := p.db.Query(query).Consistency(gocql.One).Scan(&authenticators.ID, &authenticators.UserID, &authenticators.Method, &authenticators.Secret, &authenticators.RecoveryCodes, &authenticators.VerifiedAt, &authenticators.CreatedAt, &authenticators.UpdatedAt)
if err != nil {
return nil, err
}
return &authenticators, nil
}

View File

@ -274,6 +274,13 @@ func NewProvider() (*provider, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// add authenticators table
totpCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, user_id text, method text, secret text, recovery_codes text, verified_at bigint, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.Authenticators)
err = session.Query(totpCollectionQuery).Exec()
if err != nil {
return nil, err
}
return &provider{ return &provider{
db: session, db: session,
}, err }, err

View File

@ -78,6 +78,7 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.User, fields, values) query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.User, fields, values)
err = p.db.Query(query).Exec() err = p.db.Query(query).Exec()
if err != nil { if err != nil {
return user, err return user, err
} }
@ -177,13 +178,17 @@ func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination)
// there is no offset in cassandra // there is no offset in cassandra
// so we fetch till limit + offset // so we fetch till limit + offset
// and return the results from offset to limit // and return the results from offset to limit
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User, pagination.Limit+pagination.Offset) query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User,
pagination.Limit+pagination.Offset)
scanner := p.db.Query(query).Iter().Scanner() scanner := p.db.Query(query).Iter().Scanner()
counter := int64(0) counter := int64(0)
for scanner.Next() { for scanner.Next() {
if counter >= pagination.Offset { if counter >= pagination.Offset {
var user models.User var user models.User
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.AppData, &user.CreatedAt, &user.UpdatedAt) err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods,
&user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber,
&user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled,
&user.AppData, &user.CreatedAt, &user.UpdatedAt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -297,9 +302,7 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
return err return err
} }
} }
} }
return nil return nil
} }

View File

@ -0,0 +1,81 @@
package couchbase
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/couchbase/gocb/v2"
"github.com/google/uuid"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.Key = authenticators.ID
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
insertOpt := gocb.InsertOptions{
Context: ctx,
}
_, err := p.db.Collection(models.Collections.Authenticators).Insert(authenticators.ID, authenticators, &insertOpt)
if err != nil {
return authenticators, err
}
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
authenticators.UpdatedAt = time.Now().Unix()
bytes, err := json.Marshal(authenticators)
if err != nil {
return nil, err
}
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
decoder.UseNumber()
authenticator := map[string]interface{}{}
err = decoder.Decode(&authenticator)
if err != nil {
return nil, err
}
updateFields, params := GetSetFields(authenticator)
query := fmt.Sprintf("UPDATE %s.%s SET %s WHERE _id = '%s'", p.scopeName, models.Collections.Authenticators, updateFields, authenticators.ID)
_, err = p.db.Query(query, &gocb.QueryOptions{
Context: ctx,
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
NamedParameters: params,
})
if err != nil {
return nil, err
}
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators *models.Authenticator
query := fmt.Sprintf("SELECT _id, user_id, method, secret, recovery_code, verified_at, created_at, updated_at FROM %s.%s WHERE user_id = $1 AND method = $2 LIMIT 1", p.scopeName, models.Collections.Authenticators)
q, err := p.db.Query(query, &gocb.QueryOptions{
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
Context: ctx,
PositionalParameters: []interface{}{userId, authenticatorType},
})
if err != nil {
return authenticators, err
}
err = q.One(&authenticators)
if err != nil {
return authenticators, err
}
return authenticators, nil
}

View File

@ -43,10 +43,10 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
// UpdateUser to update user information in database // UpdateUser to update user information in database
func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.User, error) { func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.User, error) {
user.UpdatedAt = time.Now().Unix() user.UpdatedAt = time.Now().Unix()
unsertOpt := gocb.UpsertOptions{ upsertOpt := gocb.UpsertOptions{
Context: ctx, Context: ctx,
} }
_, err := p.db.Collection(models.Collections.User).Upsert(user.ID, user, &unsertOpt) _, err := p.db.Collection(models.Collections.User).Upsert(user.ID, user, &upsertOpt)
if err != nil { if err != nil {
return user, err return user, err
} }

View File

@ -0,0 +1,57 @@
package dynamodb
import (
"context"
"time"
"github.com/google/uuid"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
collection := p.db.Table(models.Collections.Authenticators)
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
err := collection.Put(authenticators).RunWithContext(ctx)
if err != nil {
return authenticators, err
}
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
collection := p.db.Table(models.Collections.Authenticators)
if authenticators.ID != "" {
authenticators.UpdatedAt = time.Now().Unix()
err := UpdateByHashKey(collection, "id", authenticators.ID, authenticators)
if err != nil {
return authenticators, err
}
}
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators *models.Authenticator
collection := p.db.Table(models.Collections.Authenticators)
iter := collection.Scan().Filter("'user_id' = ?", userId).Filter("'method' = ?", authenticatorType).Iter()
for iter.NextWithContext(ctx, &authenticators) {
return authenticators, nil
}
err := iter.Err()
if err != nil {
return authenticators, err
}
return authenticators, nil
}

View File

@ -52,6 +52,7 @@ func NewProvider() (*provider, error) {
db.CreateTable(models.Collections.VerificationRequest, models.VerificationRequest{}).Wait() db.CreateTable(models.Collections.VerificationRequest, models.VerificationRequest{}).Wait()
db.CreateTable(models.Collections.Webhook, models.Webhook{}).Wait() db.CreateTable(models.Collections.Webhook, models.Webhook{}).Wait()
db.CreateTable(models.Collections.WebhookLog, models.WebhookLog{}).Wait() db.CreateTable(models.Collections.WebhookLog, models.WebhookLog{}).Wait()
db.CreateTable(models.Collections.Authenticators, models.Authenticator{}).Wait()
return &provider{ return &provider{
db: db, db: db,
}, nil }, nil

View File

@ -53,10 +53,6 @@ func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.U
if err != nil { if err != nil {
return user, err return user, err
} }
if err != nil {
return user, err
}
} }
return user, nil return user, nil
} }

View File

@ -0,0 +1,52 @@
package mongodb
import (
"context"
"time"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
authenticators.Key = authenticators.ID
authenticatorsCollection := p.db.Collection(models.Collections.Authenticators, options.Collection())
_, err := authenticatorsCollection.InsertOne(ctx, authenticators)
if err != nil {
return authenticators, err
}
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
authenticators.UpdatedAt = time.Now().Unix()
authenticatorsCollection := p.db.Collection(models.Collections.Authenticators, options.Collection())
_, err := authenticatorsCollection.UpdateOne(ctx, bson.M{"_id": bson.M{"$eq": authenticators.ID}}, bson.M{"$set": authenticators})
if err != nil {
return authenticators, err
}
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators *models.Authenticator
authenticatorsCollection := p.db.Collection(models.Collections.Authenticators, options.Collection())
err := authenticatorsCollection.FindOne(ctx, bson.M{"user_id": userId, "method": authenticatorType}).Decode(&authenticators)
if err != nil {
return authenticators, err
}
return authenticators, nil
}

View File

@ -122,6 +122,15 @@ func NewProvider() (*provider, error) {
}, },
}, options.CreateIndexes()) }, options.CreateIndexes())
mongodb.CreateCollection(ctx, models.Collections.Authenticators, options.CreateCollection())
authenticatorsCollection := mongodb.Collection(models.Collections.Authenticators, options.Collection())
authenticatorsCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
{
Keys: bson.M{"user_id": 1},
Options: options.Index().SetSparse(true),
},
}, options.CreateIndexes())
return &provider{ return &provider{
db: mongodb, db: mongodb,
}, nil }, nil

View File

@ -0,0 +1,34 @@
package provider_template
import (
"context"
"time"
"github.com/google/uuid"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
authenticators.UpdatedAt = time.Now().Unix()
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators *models.Authenticator
return authenticators, nil
}

View File

@ -26,7 +26,7 @@ type Provider interface {
// If ids set to nil / empty all the users will be updated // If ids set to nil / empty all the users will be updated
UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error
// AddVerification to save verification request in database // AddVerificationRequest to save verification request in database
AddVerificationRequest(ctx context.Context, verificationRequest *models.VerificationRequest) (*models.VerificationRequest, error) AddVerificationRequest(ctx context.Context, verificationRequest *models.VerificationRequest) (*models.VerificationRequest, error)
// GetVerificationRequestByToken to get verification request from database using token // GetVerificationRequestByToken to get verification request from database using token
GetVerificationRequestByToken(ctx context.Context, token string) (*models.VerificationRequest, error) GetVerificationRequestByToken(ctx context.Context, token string) (*models.VerificationRequest, error)
@ -53,7 +53,7 @@ type Provider interface {
AddWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error) AddWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error)
// UpdateWebhook to update webhook // UpdateWebhook to update webhook
UpdateWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error) UpdateWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error)
// ListWebhooks to list webhook // ListWebhook to list webhook
ListWebhook(ctx context.Context, pagination *model.Pagination) (*model.Webhooks, error) ListWebhook(ctx context.Context, pagination *model.Pagination) (*model.Webhooks, error)
// GetWebhookByID to get webhook by id // GetWebhookByID to get webhook by id
GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error) GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error)
@ -71,7 +71,7 @@ type Provider interface {
AddEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error) AddEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error)
// UpdateEmailTemplate to update EmailTemplate // UpdateEmailTemplate to update EmailTemplate
UpdateEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error) UpdateEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error)
// ListEmailTemplates to list EmailTemplate // ListEmailTemplate to list EmailTemplate
ListEmailTemplate(ctx context.Context, pagination *model.Pagination) (*model.EmailTemplates, error) ListEmailTemplate(ctx context.Context, pagination *model.Pagination) (*model.EmailTemplates, error)
// GetEmailTemplateByID to get EmailTemplate by id // GetEmailTemplateByID to get EmailTemplate by id
GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error)
@ -88,4 +88,15 @@ type Provider interface {
GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error)
// DeleteOTP to delete otp // DeleteOTP to delete otp
DeleteOTP(ctx context.Context, otp *models.OTP) error DeleteOTP(ctx context.Context, otp *models.OTP) error
// AddAuthenticator adds a new authenticator document to the database.
// If the authenticator doesn't have an ID, a new one is generated.
// The created document is returned, or an error if the operation fails.
AddAuthenticator(ctx context.Context, totp *models.Authenticator) (*models.Authenticator, error)
// UpdateAuthenticator updates an existing authenticator document in the database.
// The updated document is returned, or an error if the operation fails.
UpdateAuthenticator(ctx context.Context, totp *models.Authenticator) (*models.Authenticator, error)
// GetAuthenticatorDetailsByUserId retrieves details of an authenticator document based on user ID and authenticator type.
// If found, the authenticator document is returned, or an error if not found or an error occurs during the retrieval.
GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error)
} }

View File

@ -0,0 +1,55 @@
package sql
import (
"context"
"time"
"github.com/google/uuid"
"gorm.io/gorm/clause"
"github.com/authorizerdev/authorizer/server/db/models"
)
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
if exists != nil {
return authenticators, nil
}
if authenticators.ID == "" {
authenticators.ID = uuid.New().String()
}
authenticators.Key = authenticators.ID
authenticators.CreatedAt = time.Now().Unix()
authenticators.UpdatedAt = time.Now().Unix()
res := p.db.Clauses(
clause.OnConflict{
UpdateAll: true,
Columns: []clause.Column{{Name: "id"}},
}).Create(&authenticators)
if res.Error != nil {
return nil, res.Error
}
return authenticators, nil
}
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
authenticators.UpdatedAt = time.Now().Unix()
result := p.db.Save(&authenticators)
if result.Error != nil {
return authenticators, result.Error
}
return authenticators, nil
}
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
var authenticators models.Authenticator
result := p.db.Where("user_id = ?", userId).Where("method = ?", authenticatorType).First(&authenticators)
if result.Error != nil {
return nil, result.Error
}
return &authenticators, nil
}

View File

@ -77,7 +77,7 @@ func NewProvider() (*provider, error) {
logrus.Debug("Failed to drop phone number constraint:", err) logrus.Debug("Failed to drop phone number constraint:", err)
} }
err = sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{}, &models.Webhook{}, &models.WebhookLog{}, &models.EmailTemplate{}, &models.OTP{}) err = sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{}, &models.Webhook{}, &models.WebhookLog{}, &models.EmailTemplate{}, &models.OTP{}, &models.Authenticator{})
if err != nil { if err != nil {
return nil, err return nil, err
} }

37
server/env/env.go vendored
View File

@ -104,6 +104,8 @@ func InitAllEnv() error {
osDisableStrongPassword := os.Getenv(constants.EnvKeyDisableStrongPassword) osDisableStrongPassword := os.Getenv(constants.EnvKeyDisableStrongPassword)
osEnforceMultiFactorAuthentication := os.Getenv(constants.EnvKeyEnforceMultiFactorAuthentication) osEnforceMultiFactorAuthentication := os.Getenv(constants.EnvKeyEnforceMultiFactorAuthentication)
osDisableMultiFactorAuthentication := os.Getenv(constants.EnvKeyDisableMultiFactorAuthentication) osDisableMultiFactorAuthentication := os.Getenv(constants.EnvKeyDisableMultiFactorAuthentication)
osDisableTOTPLogin := os.Getenv(constants.EnvKeyDisableTOTPLogin)
osDisableMailOTPLogin := os.Getenv(constants.EnvKeyDisableMailOTPLogin)
// phone verification var // phone verification var
osDisablePhoneVerification := os.Getenv(constants.EnvKeyDisablePhoneVerification) osDisablePhoneVerification := os.Getenv(constants.EnvKeyDisablePhoneVerification)
osDisablePlayground := os.Getenv(constants.EnvKeyDisablePlayGround) osDisablePlayground := os.Getenv(constants.EnvKeyDisablePlayGround)
@ -689,20 +691,13 @@ func InitAllEnv() error {
envData[constants.EnvKeyDisableEmailVerification] = true envData[constants.EnvKeyDisableEmailVerification] = true
envData[constants.EnvKeyDisableMagicLinkLogin] = true envData[constants.EnvKeyDisableMagicLinkLogin] = true
envData[constants.EnvKeyIsEmailServiceEnabled] = false envData[constants.EnvKeyIsEmailServiceEnabled] = false
envData[constants.EnvKeyDisableMailOTPLogin] = true
} }
if envData[constants.EnvKeySmtpHost] != "" && envData[constants.EnvKeySmtpUsername] != "" && envData[constants.EnvKeySmtpPassword] != "" && envData[constants.EnvKeySenderEmail] != "" && envData[constants.EnvKeySmtpPort] != "" { if envData[constants.EnvKeySmtpHost] != "" && envData[constants.EnvKeySmtpUsername] != "" && envData[constants.EnvKeySmtpPassword] != "" && envData[constants.EnvKeySenderEmail] != "" && envData[constants.EnvKeySmtpPort] != "" {
envData[constants.EnvKeyIsEmailServiceEnabled] = true 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) { if envData[constants.EnvKeyDisableEmailVerification].(bool) {
envData[constants.EnvKeyDisableMagicLinkLogin] = true envData[constants.EnvKeyDisableMagicLinkLogin] = true
} }
@ -840,6 +835,32 @@ func InitAllEnv() error {
} }
} }
if _, ok := envData[constants.EnvKeyDisableTOTPLogin]; !ok {
envData[constants.EnvKeyDisableTOTPLogin] = osDisableTOTPLogin == "false"
}
if osDisableTOTPLogin != "" {
boolValue, err := strconv.ParseBool(osDisableTOTPLogin)
if err != nil {
return err
}
if boolValue != envData[constants.EnvKeyDisableTOTPLogin].(bool) {
envData[constants.EnvKeyDisableTOTPLogin] = boolValue
}
}
if _, ok := envData[constants.EnvKeyDisableMailOTPLogin]; !ok {
envData[constants.EnvKeyDisableMailOTPLogin] = osDisableMailOTPLogin == "true"
}
if osDisableMailOTPLogin != "" {
boolValue, err := strconv.ParseBool(osDisableMailOTPLogin)
if err != nil {
return err
}
if boolValue != envData[constants.EnvKeyDisableMailOTPLogin].(bool) {
envData[constants.EnvKeyDisableMailOTPLogin] = boolValue
}
}
err = memorystore.Provider.UpdateEnvStore(envData) err = memorystore.Provider.UpdateEnvStore(envData)
if err != nil { if err != nil {
log.Debug("Error while updating env store: ", err) log.Debug("Error while updating env store: ", err)

View File

@ -196,7 +196,7 @@ func PersistEnv() error {
envValue := strings.TrimSpace(os.Getenv(key)) envValue := strings.TrimSpace(os.Getenv(key))
if envValue != "" { if envValue != "" {
switch key { switch key {
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyIsSMSServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure, constants.EnvKeyDisablePhoneVerification, constants.EnvKeyDisablePlayGround: case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyIsSMSServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure, constants.EnvKeyDisablePhoneVerification, constants.EnvKeyDisablePlayGround, constants.EnvKeyDisableTOTPLogin, constants.EnvKeyDisableMailOTPLogin:
if envValueBool, err := strconv.ParseBool(envValue); err == nil { if envValueBool, err := strconv.ParseBool(envValue); err == nil {
if value.(bool) != envValueBool { if value.(bool) != envValueBool {
storeData[key] = envValueBool storeData[key] = envValueBool
@ -227,6 +227,11 @@ func PersistEnv() error {
storeData[constants.EnvKeyDisableMagicLinkLogin] = true storeData[constants.EnvKeyDisableMagicLinkLogin] = true
hasChanged = true hasChanged = true
} }
if !storeData[constants.EnvKeyDisableMailOTPLogin].(bool) {
storeData[constants.EnvKeyDisableMailOTPLogin] = true
hasChanged = true
}
} }
err = memorystore.Provider.UpdateEnvStore(storeData) err = memorystore.Provider.UpdateEnvStore(storeData)

View File

@ -5,7 +5,7 @@ go 1.16
require ( require (
github.com/99designs/gqlgen v0.17.39 github.com/99designs/gqlgen v0.17.39
github.com/arangodb/go-driver v1.6.0 github.com/arangodb/go-driver v1.6.0
github.com/aws/aws-sdk-go v1.45.25 github.com/aws/aws-sdk-go v1.47.4
github.com/bytedance/sonic v1.10.2 // indirect github.com/bytedance/sonic v1.10.2 // indirect
github.com/coreos/go-oidc/v3 v3.6.0 github.com/coreos/go-oidc/v3 v3.6.0
github.com/couchbase/gocb/v2 v2.6.4 github.com/couchbase/gocb/v2 v2.6.4
@ -16,6 +16,7 @@ require (
github.com/go-playground/validator/v10 v10.15.5 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/gocql/gocql v1.6.0 github.com/gocql/gocql v1.6.0
github.com/gokyle/twofactor v1.0.1
github.com/golang-jwt/jwt v3.2.2+incompatible github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.3.1 github.com/google/uuid v1.3.1
github.com/guregu/dynamo v1.20.2 github.com/guregu/dynamo v1.20.2
@ -26,11 +27,13 @@ require (
github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect github.com/montanaflynn/stats v0.7.1 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pquerna/otp v1.4.0
github.com/redis/go-redis/v9 v9.2.1 github.com/redis/go-redis/v9 v9.2.1
github.com/robertkrimen/otto v0.2.1 github.com/robertkrimen/otto v0.2.1
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/sosodev/duration v1.2.0 // indirect github.com/sosodev/duration v1.2.0 // indirect
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d
github.com/twilio/twilio-go v1.14.1 github.com/twilio/twilio-go v1.14.1
github.com/urfave/cli/v2 v2.25.7 // indirect github.com/urfave/cli/v2 v2.25.7 // indirect
github.com/vektah/gqlparser/v2 v2.5.10 github.com/vektah/gqlparser/v2 v2.5.10

View File

@ -643,14 +643,18 @@ github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e/go.mod h1:m
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/aws/aws-sdk-go v1.44.306/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go v1.44.306/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.45.25 h1:c4fLlh5sLdK2DCRTY1z0hyuJZU4ygxX8m1FswL6/nF4= github.com/aws/aws-sdk-go v1.47.4 h1:IyhNbmPt+5ldi5HNzv7ZnXiqSglDMaJiZlzj4Yq3qnk=
github.com/aws/aws-sdk-go v1.45.25/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go v1.47.4/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY=
github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
@ -788,6 +792,8 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gocql/gocql v1.6.0 h1:IdFdOTbnpbd0pDhl4REKQDM+Q0SzKXQ1Yh+YZZ8T/qU= github.com/gocql/gocql v1.6.0 h1:IdFdOTbnpbd0pDhl4REKQDM+Q0SzKXQ1Yh+YZZ8T/qU=
github.com/gocql/gocql v1.6.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/gocql/gocql v1.6.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
github.com/gokyle/twofactor v1.0.1 h1:uRhvx0S4Hb82RPIDALnf7QxbmPL49LyyaCkJDpWx+Ek=
github.com/gokyle/twofactor v1.0.1/go.mod h1:4gxzH1eaE/F3Pct/sCDNOylP0ClofUO5j4XZN9tKtLE=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
@ -997,6 +1003,8 @@ github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxA
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
github.com/maruel/rs v1.1.0 h1:dh4OceAF5yD06EASOrb+DS358LI4g0B90YApSdjCP6U=
github.com/maruel/rs v1.1.0/go.mod h1:vzwMjzSJJxLIXmU62qHj6O5QRn5kvCKxFrfaFCxBcUY=
github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@ -1040,6 +1048,8 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
@ -1089,6 +1099,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d h1:4x1FeGJRB00cvxnKXnRJDT89fvG/Lzm2ecm0vlr/qDs=
github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d/go.mod h1:uSELzeIcTceNCgzbKdJuJa0ouCqqtkyzL+6bnA3rM+M=
github.com/twilio/twilio-go v1.14.1 h1:uyMwNe2naFKwxLpVflAHbKEPiW9iHNI8VF6NWLJJ1Kk= github.com/twilio/twilio-go v1.14.1 h1:uyMwNe2naFKwxLpVflAHbKEPiW9iHNI8VF6NWLJJ1Kk=
github.com/twilio/twilio-go v1.14.1/go.mod h1:tdnfQ5TjbewoAu4lf9bMsGvfuJ/QU9gYuv9yx3TSIXU= github.com/twilio/twilio-go v1.14.1/go.mod h1:tdnfQ5TjbewoAu4lf9bMsGvfuJ/QU9gYuv9yx3TSIXU=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
@ -1929,5 +1941,7 @@ modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@ -48,12 +48,16 @@ type DirectiveRoot struct {
type ComplexityRoot struct { type ComplexityRoot struct {
AuthResponse struct { AuthResponse struct {
AccessToken func(childComplexity int) int AccessToken func(childComplexity int) int
AuthenticatorRecoveryCodes func(childComplexity int) int
AuthenticatorScannerImage func(childComplexity int) int
AuthenticatorSecret func(childComplexity int) int
ExpiresIn func(childComplexity int) int ExpiresIn func(childComplexity int) int
IDToken func(childComplexity int) int IDToken func(childComplexity int) int
Message func(childComplexity int) int Message func(childComplexity int) int
RefreshToken func(childComplexity int) int RefreshToken func(childComplexity int) int
ShouldShowEmailOtpScreen func(childComplexity int) int ShouldShowEmailOtpScreen func(childComplexity int) int
ShouldShowMobileOtpScreen func(childComplexity int) int ShouldShowMobileOtpScreen func(childComplexity int) int
ShouldShowTotpScreen func(childComplexity int) int
User func(childComplexity int) int User func(childComplexity int) int
} }
@ -98,11 +102,13 @@ type ComplexityRoot struct {
DisableEmailVerification func(childComplexity int) int DisableEmailVerification func(childComplexity int) int
DisableLoginPage func(childComplexity int) int DisableLoginPage func(childComplexity int) int
DisableMagicLinkLogin func(childComplexity int) int DisableMagicLinkLogin func(childComplexity int) int
DisableMailOtpLogin func(childComplexity int) int
DisableMultiFactorAuthentication func(childComplexity int) int DisableMultiFactorAuthentication func(childComplexity int) int
DisablePlayground func(childComplexity int) int DisablePlayground func(childComplexity int) int
DisableRedisForEnv func(childComplexity int) int DisableRedisForEnv func(childComplexity int) int
DisableSignUp func(childComplexity int) int DisableSignUp func(childComplexity int) int
DisableStrongPassword func(childComplexity int) int DisableStrongPassword func(childComplexity int) int
DisableTotpLogin func(childComplexity int) int
EnforceMultiFactorAuthentication func(childComplexity int) int EnforceMultiFactorAuthentication func(childComplexity int) int
FacebookClientID func(childComplexity int) int FacebookClientID func(childComplexity int) int
FacebookClientSecret func(childComplexity int) int FacebookClientSecret func(childComplexity int) int
@ -412,6 +418,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.AuthResponse.AccessToken(childComplexity), true return e.complexity.AuthResponse.AccessToken(childComplexity), true
case "AuthResponse.authenticator_recovery_codes":
if e.complexity.AuthResponse.AuthenticatorRecoveryCodes == nil {
break
}
return e.complexity.AuthResponse.AuthenticatorRecoveryCodes(childComplexity), true
case "AuthResponse.authenticator_scanner_image":
if e.complexity.AuthResponse.AuthenticatorScannerImage == nil {
break
}
return e.complexity.AuthResponse.AuthenticatorScannerImage(childComplexity), true
case "AuthResponse.authenticator_secret":
if e.complexity.AuthResponse.AuthenticatorSecret == nil {
break
}
return e.complexity.AuthResponse.AuthenticatorSecret(childComplexity), true
case "AuthResponse.expires_in": case "AuthResponse.expires_in":
if e.complexity.AuthResponse.ExpiresIn == nil { if e.complexity.AuthResponse.ExpiresIn == nil {
break break
@ -454,6 +481,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.AuthResponse.ShouldShowMobileOtpScreen(childComplexity), true return e.complexity.AuthResponse.ShouldShowMobileOtpScreen(childComplexity), true
case "AuthResponse.should_show_totp_screen":
if e.complexity.AuthResponse.ShouldShowTotpScreen == nil {
break
}
return e.complexity.AuthResponse.ShouldShowTotpScreen(childComplexity), true
case "AuthResponse.user": case "AuthResponse.user":
if e.complexity.AuthResponse.User == nil { if e.complexity.AuthResponse.User == nil {
break break
@ -699,6 +733,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.DisableMagicLinkLogin(childComplexity), true return e.complexity.Env.DisableMagicLinkLogin(childComplexity), true
case "Env.DISABLE_MAIL_OTP_LOGIN":
if e.complexity.Env.DisableMailOtpLogin == nil {
break
}
return e.complexity.Env.DisableMailOtpLogin(childComplexity), true
case "Env.DISABLE_MULTI_FACTOR_AUTHENTICATION": case "Env.DISABLE_MULTI_FACTOR_AUTHENTICATION":
if e.complexity.Env.DisableMultiFactorAuthentication == nil { if e.complexity.Env.DisableMultiFactorAuthentication == nil {
break break
@ -734,6 +775,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.DisableStrongPassword(childComplexity), true return e.complexity.Env.DisableStrongPassword(childComplexity), true
case "Env.DISABLE_TOTP_LOGIN":
if e.complexity.Env.DisableTotpLogin == nil {
break
}
return e.complexity.Env.DisableTotpLogin(childComplexity), true
case "Env.ENFORCE_MULTI_FACTOR_AUTHENTICATION": case "Env.ENFORCE_MULTI_FACTOR_AUTHENTICATION":
if e.complexity.Env.EnforceMultiFactorAuthentication == nil { if e.complexity.Env.EnforceMultiFactorAuthentication == nil {
break break
@ -2349,11 +2397,19 @@ type AuthResponse {
message: String! message: String!
should_show_email_otp_screen: Boolean should_show_email_otp_screen: Boolean
should_show_mobile_otp_screen: Boolean should_show_mobile_otp_screen: Boolean
should_show_totp_screen: Boolean
access_token: String access_token: String
id_token: String id_token: String
refresh_token: String refresh_token: String
expires_in: Int64 expires_in: Int64
user: User user: User
# key for totp login
# it is a base64 image url
authenticator_scanner_image: String
# string which can be used instead of scanner image
authenticator_secret: String
# recovery codes for totp login shared with user only once
authenticator_recovery_codes: [String]
} }
type Response { type Response {
@ -2428,6 +2484,8 @@ type Env {
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
DEFAULT_AUTHORIZE_RESPONSE_MODE: String DEFAULT_AUTHORIZE_RESPONSE_MODE: String
DISABLE_PLAYGROUND: Boolean! DISABLE_PLAYGROUND: Boolean!
DISABLE_MAIL_OTP_LOGIN: Boolean!
DISABLE_TOTP_LOGIN: Boolean!
} }
type ValidateJWTTokenResponse { type ValidateJWTTokenResponse {
@ -2551,6 +2609,8 @@ input UpdateEnvInput {
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
DEFAULT_AUTHORIZE_RESPONSE_MODE: String DEFAULT_AUTHORIZE_RESPONSE_MODE: String
DISABLE_PLAYGROUND: Boolean DISABLE_PLAYGROUND: Boolean
DISABLE_MAIL_OTP_LOGIN: Boolean
DISABLE_TOTP_LOGIN: Boolean
} }
input AdminLoginInput { input AdminLoginInput {
@ -2806,10 +2866,11 @@ input DeleteEmailTemplateRequest {
} }
input VerifyOTPRequest { input VerifyOTPRequest {
# either email or phone_number is required # either email, phone_number or totp_token is required
email: String email: String
phone_number: String phone_number: String
otp: String! otp: String!
totp: Boolean
# state is used for authorization code grant flow # state is used for authorization code grant flow
# 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
@ -3657,6 +3718,47 @@ func (ec *executionContext) fieldContext_AuthResponse_should_show_mobile_otp_scr
return fc, nil 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) { 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) fc, err := ec.fieldContext_AuthResponse_access_token(ctx, field)
if err != nil { if err != nil {
@ -3904,6 +4006,129 @@ func (ec *executionContext) fieldContext_AuthResponse_user(ctx context.Context,
return fc, nil return fc, nil
} }
func (ec *executionContext) _AuthResponse_authenticator_scanner_image(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_AuthResponse_authenticator_scanner_image(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.AuthenticatorScannerImage, 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_authenticator_scanner_image(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) _AuthResponse_authenticator_secret(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_AuthResponse_authenticator_secret(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.AuthenticatorSecret, 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_authenticator_secret(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) _AuthResponse_authenticator_recovery_codes(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_AuthResponse_authenticator_recovery_codes(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.AuthenticatorRecoveryCodes, 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_authenticator_recovery_codes(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) { 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) fc, err := ec.fieldContext_EmailTemplate_id(ctx, field)
if err != nil { if err != nil {
@ -6904,6 +7129,94 @@ func (ec *executionContext) fieldContext_Env_DISABLE_PLAYGROUND(ctx context.Cont
return fc, nil return fc, nil
} }
func (ec *executionContext) _Env_DISABLE_MAIL_OTP_LOGIN(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_DISABLE_MAIL_OTP_LOGIN(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.DisableMailOtpLogin, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Env_DISABLE_MAIL_OTP_LOGIN(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Env",
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) _Env_DISABLE_TOTP_LOGIN(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_DISABLE_TOTP_LOGIN(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.DisableTotpLogin, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Env_DISABLE_TOTP_LOGIN(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Env",
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) _Error_message(ctx context.Context, field graphql.CollectedField, obj *model.Error) (ret graphql.Marshaler) { func (ec *executionContext) _Error_message(ctx context.Context, field graphql.CollectedField, obj *model.Error) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Error_message(ctx, field) fc, err := ec.fieldContext_Error_message(ctx, field)
if err != nil { if err != nil {
@ -7950,6 +8263,8 @@ func (ec *executionContext) fieldContext_Mutation_signup(ctx context.Context, fi
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen": case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token": case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field) return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token": case "id_token":
@ -7960,6 +8275,12 @@ func (ec *executionContext) fieldContext_Mutation_signup(ctx context.Context, fi
return ec.fieldContext_AuthResponse_expires_in(ctx, field) return ec.fieldContext_AuthResponse_expires_in(ctx, field)
case "user": case "user":
return ec.fieldContext_AuthResponse_user(ctx, field) return ec.fieldContext_AuthResponse_user(ctx, field)
case "authenticator_scanner_image":
return ec.fieldContext_AuthResponse_authenticator_scanner_image(ctx, field)
case "authenticator_secret":
return ec.fieldContext_AuthResponse_authenticator_secret(ctx, field)
case "authenticator_recovery_codes":
return ec.fieldContext_AuthResponse_authenticator_recovery_codes(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name) return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
}, },
@ -8023,6 +8344,8 @@ func (ec *executionContext) fieldContext_Mutation_mobile_signup(ctx context.Cont
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen": case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token": case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field) return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token": case "id_token":
@ -8033,6 +8356,12 @@ func (ec *executionContext) fieldContext_Mutation_mobile_signup(ctx context.Cont
return ec.fieldContext_AuthResponse_expires_in(ctx, field) return ec.fieldContext_AuthResponse_expires_in(ctx, field)
case "user": case "user":
return ec.fieldContext_AuthResponse_user(ctx, field) return ec.fieldContext_AuthResponse_user(ctx, field)
case "authenticator_scanner_image":
return ec.fieldContext_AuthResponse_authenticator_scanner_image(ctx, field)
case "authenticator_secret":
return ec.fieldContext_AuthResponse_authenticator_secret(ctx, field)
case "authenticator_recovery_codes":
return ec.fieldContext_AuthResponse_authenticator_recovery_codes(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name) return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
}, },
@ -8096,6 +8425,8 @@ func (ec *executionContext) fieldContext_Mutation_login(ctx context.Context, fie
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen": case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token": case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field) return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token": case "id_token":
@ -8106,6 +8437,12 @@ func (ec *executionContext) fieldContext_Mutation_login(ctx context.Context, fie
return ec.fieldContext_AuthResponse_expires_in(ctx, field) return ec.fieldContext_AuthResponse_expires_in(ctx, field)
case "user": case "user":
return ec.fieldContext_AuthResponse_user(ctx, field) return ec.fieldContext_AuthResponse_user(ctx, field)
case "authenticator_scanner_image":
return ec.fieldContext_AuthResponse_authenticator_scanner_image(ctx, field)
case "authenticator_secret":
return ec.fieldContext_AuthResponse_authenticator_secret(ctx, field)
case "authenticator_recovery_codes":
return ec.fieldContext_AuthResponse_authenticator_recovery_codes(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name) return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
}, },
@ -8169,6 +8506,8 @@ func (ec *executionContext) fieldContext_Mutation_mobile_login(ctx context.Conte
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen": case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token": case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field) return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token": case "id_token":
@ -8179,6 +8518,12 @@ func (ec *executionContext) fieldContext_Mutation_mobile_login(ctx context.Conte
return ec.fieldContext_AuthResponse_expires_in(ctx, field) return ec.fieldContext_AuthResponse_expires_in(ctx, field)
case "user": case "user":
return ec.fieldContext_AuthResponse_user(ctx, field) return ec.fieldContext_AuthResponse_user(ctx, field)
case "authenticator_scanner_image":
return ec.fieldContext_AuthResponse_authenticator_scanner_image(ctx, field)
case "authenticator_secret":
return ec.fieldContext_AuthResponse_authenticator_secret(ctx, field)
case "authenticator_recovery_codes":
return ec.fieldContext_AuthResponse_authenticator_recovery_codes(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name) return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
}, },
@ -8408,6 +8753,8 @@ func (ec *executionContext) fieldContext_Mutation_verify_email(ctx context.Conte
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen": case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token": case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field) return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token": case "id_token":
@ -8418,6 +8765,12 @@ func (ec *executionContext) fieldContext_Mutation_verify_email(ctx context.Conte
return ec.fieldContext_AuthResponse_expires_in(ctx, field) return ec.fieldContext_AuthResponse_expires_in(ctx, field)
case "user": case "user":
return ec.fieldContext_AuthResponse_user(ctx, field) return ec.fieldContext_AuthResponse_user(ctx, field)
case "authenticator_scanner_image":
return ec.fieldContext_AuthResponse_authenticator_scanner_image(ctx, field)
case "authenticator_secret":
return ec.fieldContext_AuthResponse_authenticator_secret(ctx, field)
case "authenticator_recovery_codes":
return ec.fieldContext_AuthResponse_authenticator_recovery_codes(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name) return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
}, },
@ -8717,6 +9070,8 @@ func (ec *executionContext) fieldContext_Mutation_verify_otp(ctx context.Context
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen": case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token": case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field) return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token": case "id_token":
@ -8727,6 +9082,12 @@ func (ec *executionContext) fieldContext_Mutation_verify_otp(ctx context.Context
return ec.fieldContext_AuthResponse_expires_in(ctx, field) return ec.fieldContext_AuthResponse_expires_in(ctx, field)
case "user": case "user":
return ec.fieldContext_AuthResponse_user(ctx, field) return ec.fieldContext_AuthResponse_user(ctx, field)
case "authenticator_scanner_image":
return ec.fieldContext_AuthResponse_authenticator_scanner_image(ctx, field)
case "authenticator_secret":
return ec.fieldContext_AuthResponse_authenticator_secret(ctx, field)
case "authenticator_recovery_codes":
return ec.fieldContext_AuthResponse_authenticator_recovery_codes(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name) return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
}, },
@ -10187,6 +10548,8 @@ func (ec *executionContext) fieldContext_Query_session(ctx context.Context, fiel
return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_email_otp_screen(ctx, field)
case "should_show_mobile_otp_screen": case "should_show_mobile_otp_screen":
return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field) return ec.fieldContext_AuthResponse_should_show_mobile_otp_screen(ctx, field)
case "should_show_totp_screen":
return ec.fieldContext_AuthResponse_should_show_totp_screen(ctx, field)
case "access_token": case "access_token":
return ec.fieldContext_AuthResponse_access_token(ctx, field) return ec.fieldContext_AuthResponse_access_token(ctx, field)
case "id_token": case "id_token":
@ -10197,6 +10560,12 @@ func (ec *executionContext) fieldContext_Query_session(ctx context.Context, fiel
return ec.fieldContext_AuthResponse_expires_in(ctx, field) return ec.fieldContext_AuthResponse_expires_in(ctx, field)
case "user": case "user":
return ec.fieldContext_AuthResponse_user(ctx, field) return ec.fieldContext_AuthResponse_user(ctx, field)
case "authenticator_scanner_image":
return ec.fieldContext_AuthResponse_authenticator_scanner_image(ctx, field)
case "authenticator_secret":
return ec.fieldContext_AuthResponse_authenticator_secret(ctx, field)
case "authenticator_recovery_codes":
return ec.fieldContext_AuthResponse_authenticator_recovery_codes(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name) return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
}, },
@ -10853,6 +11222,10 @@ func (ec *executionContext) fieldContext_Query__env(ctx context.Context, field g
return ec.fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_MODE(ctx, field) return ec.fieldContext_Env_DEFAULT_AUTHORIZE_RESPONSE_MODE(ctx, field)
case "DISABLE_PLAYGROUND": case "DISABLE_PLAYGROUND":
return ec.fieldContext_Env_DISABLE_PLAYGROUND(ctx, field) return ec.fieldContext_Env_DISABLE_PLAYGROUND(ctx, field)
case "DISABLE_MAIL_OTP_LOGIN":
return ec.fieldContext_Env_DISABLE_MAIL_OTP_LOGIN(ctx, field)
case "DISABLE_TOTP_LOGIN":
return ec.fieldContext_Env_DISABLE_TOTP_LOGIN(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type Env", field.Name) return nil, fmt.Errorf("no field named %q was found under type Env", field.Name)
}, },
@ -17342,7 +17715,7 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SMTP_LOCAL_NAME", "SENDER_EMAIL", "SENDER_NAME", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "MICROSOFT_CLIENT_ID", "MICROSOFT_CLIENT_SECRET", "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID", "ORGANIZATION_NAME", "ORGANIZATION_LOGO", "DEFAULT_AUTHORIZE_RESPONSE_TYPE", "DEFAULT_AUTHORIZE_RESPONSE_MODE", "DISABLE_PLAYGROUND"} fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SMTP_LOCAL_NAME", "SENDER_EMAIL", "SENDER_NAME", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "MICROSOFT_CLIENT_ID", "MICROSOFT_CLIENT_SECRET", "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID", "ORGANIZATION_NAME", "ORGANIZATION_LOGO", "DEFAULT_AUTHORIZE_RESPONSE_TYPE", "DEFAULT_AUTHORIZE_RESPONSE_MODE", "DISABLE_PLAYGROUND", "DISABLE_MAIL_OTP_LOGIN", "DISABLE_TOTP_LOGIN"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@ -17826,6 +18199,24 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
return it, err return it, err
} }
it.DisablePlayground = data it.DisablePlayground = data
case "DISABLE_MAIL_OTP_LOGIN":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DISABLE_MAIL_OTP_LOGIN"))
data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
it.DisableMailOtpLogin = data
case "DISABLE_TOTP_LOGIN":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DISABLE_TOTP_LOGIN"))
data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
it.DisableTotpLogin = data
} }
} }
@ -18328,7 +18719,7 @@ func (ec *executionContext) unmarshalInputVerifyOTPRequest(ctx context.Context,
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"email", "phone_number", "otp", "state"} fieldsInOrder := [...]string{"email", "phone_number", "otp", "totp", "state"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@ -18362,6 +18753,15 @@ func (ec *executionContext) unmarshalInputVerifyOTPRequest(ctx context.Context,
return it, err return it, err
} }
it.Otp = data it.Otp = data
case "totp":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("totp"))
data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
it.Totp = data
case "state": case "state":
var err error var err error
@ -18434,6 +18834,8 @@ func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.Selection
out.Values[i] = ec._AuthResponse_should_show_email_otp_screen(ctx, field, obj) out.Values[i] = ec._AuthResponse_should_show_email_otp_screen(ctx, field, obj)
case "should_show_mobile_otp_screen": case "should_show_mobile_otp_screen":
out.Values[i] = ec._AuthResponse_should_show_mobile_otp_screen(ctx, field, obj) out.Values[i] = ec._AuthResponse_should_show_mobile_otp_screen(ctx, field, obj)
case "should_show_totp_screen":
out.Values[i] = ec._AuthResponse_should_show_totp_screen(ctx, field, obj)
case "access_token": case "access_token":
out.Values[i] = ec._AuthResponse_access_token(ctx, field, obj) out.Values[i] = ec._AuthResponse_access_token(ctx, field, obj)
case "id_token": case "id_token":
@ -18444,6 +18846,12 @@ func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.Selection
out.Values[i] = ec._AuthResponse_expires_in(ctx, field, obj) out.Values[i] = ec._AuthResponse_expires_in(ctx, field, obj)
case "user": case "user":
out.Values[i] = ec._AuthResponse_user(ctx, field, obj) out.Values[i] = ec._AuthResponse_user(ctx, field, obj)
case "authenticator_scanner_image":
out.Values[i] = ec._AuthResponse_authenticator_scanner_image(ctx, field, obj)
case "authenticator_secret":
out.Values[i] = ec._AuthResponse_authenticator_secret(ctx, field, obj)
case "authenticator_recovery_codes":
out.Values[i] = ec._AuthResponse_authenticator_recovery_codes(ctx, field, obj)
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@ -18751,6 +19159,16 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
out.Invalids++ out.Invalids++
} }
case "DISABLE_MAIL_OTP_LOGIN":
out.Values[i] = ec._Env_DISABLE_MAIL_OTP_LOGIN(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "DISABLE_TOTP_LOGIN":
out.Values[i] = ec._Env_DISABLE_TOTP_LOGIN(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }

View File

@ -29,11 +29,15 @@ type AuthResponse struct {
Message string `json:"message"` Message string `json:"message"`
ShouldShowEmailOtpScreen *bool `json:"should_show_email_otp_screen,omitempty"` ShouldShowEmailOtpScreen *bool `json:"should_show_email_otp_screen,omitempty"`
ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_otp_screen,omitempty"` ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_otp_screen,omitempty"`
ShouldShowTotpScreen *bool `json:"should_show_totp_screen,omitempty"`
AccessToken *string `json:"access_token,omitempty"` AccessToken *string `json:"access_token,omitempty"`
IDToken *string `json:"id_token,omitempty"` IDToken *string `json:"id_token,omitempty"`
RefreshToken *string `json:"refresh_token,omitempty"` RefreshToken *string `json:"refresh_token,omitempty"`
ExpiresIn *int64 `json:"expires_in,omitempty"` ExpiresIn *int64 `json:"expires_in,omitempty"`
User *User `json:"user,omitempty"` User *User `json:"user,omitempty"`
AuthenticatorScannerImage *string `json:"authenticator_scanner_image,omitempty"`
AuthenticatorSecret *string `json:"authenticator_secret,omitempty"`
AuthenticatorRecoveryCodes []*string `json:"authenticator_recovery_codes,omitempty"`
} }
type DeleteEmailTemplateRequest struct { type DeleteEmailTemplateRequest struct {
@ -122,6 +126,8 @@ type Env struct {
DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE,omitempty"` DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE,omitempty"`
DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE,omitempty"` DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE,omitempty"`
DisablePlayground bool `json:"DISABLE_PLAYGROUND"` DisablePlayground bool `json:"DISABLE_PLAYGROUND"`
DisableMailOtpLogin bool `json:"DISABLE_MAIL_OTP_LOGIN"`
DisableTotpLogin bool `json:"DISABLE_TOTP_LOGIN"`
} }
type Error struct { type Error struct {
@ -382,6 +388,8 @@ type UpdateEnvInput struct {
DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE,omitempty"` DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE,omitempty"`
DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE,omitempty"` DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE,omitempty"`
DisablePlayground *bool `json:"DISABLE_PLAYGROUND,omitempty"` DisablePlayground *bool `json:"DISABLE_PLAYGROUND,omitempty"`
DisableMailOtpLogin *bool `json:"DISABLE_MAIL_OTP_LOGIN,omitempty"`
DisableTotpLogin *bool `json:"DISABLE_TOTP_LOGIN,omitempty"`
} }
type UpdateProfileInput struct { type UpdateProfileInput struct {
@ -502,6 +510,7 @@ type VerifyOTPRequest struct {
Email *string `json:"email,omitempty"` Email *string `json:"email,omitempty"`
PhoneNumber *string `json:"phone_number,omitempty"` PhoneNumber *string `json:"phone_number,omitempty"`
Otp string `json:"otp"` Otp string `json:"otp"`
Totp *bool `json:"totp,omitempty"`
State *string `json:"state,omitempty"` State *string `json:"state,omitempty"`
} }

View File

@ -95,11 +95,19 @@ type AuthResponse {
message: String! message: String!
should_show_email_otp_screen: Boolean should_show_email_otp_screen: Boolean
should_show_mobile_otp_screen: Boolean should_show_mobile_otp_screen: Boolean
should_show_totp_screen: Boolean
access_token: String access_token: String
id_token: String id_token: String
refresh_token: String refresh_token: String
expires_in: Int64 expires_in: Int64
user: User user: User
# key for totp login
# it is a base64 image url
authenticator_scanner_image: String
# string which can be used instead of scanner image
authenticator_secret: String
# recovery codes for totp login shared with user only once
authenticator_recovery_codes: [String]
} }
type Response { type Response {
@ -174,6 +182,8 @@ type Env {
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
DEFAULT_AUTHORIZE_RESPONSE_MODE: String DEFAULT_AUTHORIZE_RESPONSE_MODE: String
DISABLE_PLAYGROUND: Boolean! DISABLE_PLAYGROUND: Boolean!
DISABLE_MAIL_OTP_LOGIN: Boolean!
DISABLE_TOTP_LOGIN: Boolean!
} }
type ValidateJWTTokenResponse { type ValidateJWTTokenResponse {
@ -297,6 +307,8 @@ input UpdateEnvInput {
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
DEFAULT_AUTHORIZE_RESPONSE_MODE: String DEFAULT_AUTHORIZE_RESPONSE_MODE: String
DISABLE_PLAYGROUND: Boolean DISABLE_PLAYGROUND: Boolean
DISABLE_MAIL_OTP_LOGIN: Boolean
DISABLE_TOTP_LOGIN: Boolean
} }
input AdminLoginInput { input AdminLoginInput {
@ -552,10 +564,11 @@ input DeleteEmailTemplateRequest {
} }
input VerifyOTPRequest { input VerifyOTPRequest {
# either email or phone_number is required # either email, phone_number or totp_token is required
email: String email: String
phone_number: String phone_number: String
otp: String! otp: String!
totp: Boolean
# state is used for authorization code grant flow # state is used for authorization code grant flow
# 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

View File

@ -2,6 +2,7 @@ package main
import ( import (
"flag" "flag"
"github.com/authorizerdev/authorizer/server/authenticators"
"github.com/authorizerdev/authorizer/server/cli" "github.com/authorizerdev/authorizer/server/cli"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
@ -70,6 +71,11 @@ func main() {
log.Fatalln("Error while initializing oauth: ", err) log.Fatalln("Error while initializing oauth: ", err)
} }
err = authenticators.InitTOTPStore()
if err != nil {
log.Fatalln("Error while initializing authenticator: ", err)
}
router := routes.InitRouter(log) router := routes.InitRouter(log)
log.Info("Starting Authorizer: ", VERSION) log.Info("Starting Authorizer: ", VERSION)
port, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyPort) port, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyPort)

View File

@ -36,9 +36,11 @@ func InitMemStore() error {
constants.EnvKeyIsSMSServiceEnabled: false, constants.EnvKeyIsSMSServiceEnabled: false,
constants.EnvKeyEnforceMultiFactorAuthentication: false, constants.EnvKeyEnforceMultiFactorAuthentication: false,
constants.EnvKeyDisableMultiFactorAuthentication: false, constants.EnvKeyDisableMultiFactorAuthentication: false,
constants.EnvKeyDisableTOTPLogin: false,
constants.EnvKeyAppCookieSecure: true, constants.EnvKeyAppCookieSecure: true,
constants.EnvKeyAdminCookieSecure: true, constants.EnvKeyAdminCookieSecure: true,
constants.EnvKeyDisablePlayGround: true, constants.EnvKeyDisablePlayGround: true,
constants.EnvKeyDisableMailOTPLogin: true,
} }
requiredEnvs := RequiredEnvStoreObj.GetRequiredEnv() requiredEnvs := RequiredEnvStoreObj.GetRequiredEnv()

View File

@ -176,7 +176,7 @@ func (c *provider) GetEnvStore() (map[string]interface{}, error) {
return nil, err return nil, err
} }
for key, value := range data { for key, value := range data {
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyIsSMSServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure || key == constants.EnvKeyDisablePlayGround { if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyIsSMSServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure || key == constants.EnvKeyDisablePlayGround || key == constants.EnvKeyDisableTOTPLogin || key == constants.EnvKeyDisableMailOTPLogin {
boolValue, err := strconv.ParseBool(value) boolValue, err := strconv.ParseBool(value)
if err != nil { if err != nil {
return res, err return res, err

View File

@ -38,7 +38,7 @@ type RequiredEnv struct {
CouchbaseBucketRAMQuotaMB string `json:"COUCHBASE_BUCKET_RAM_QUOTA"` CouchbaseBucketRAMQuotaMB string `json:"COUCHBASE_BUCKET_RAM_QUOTA"`
} }
// RequiredEnvObj is a simple in-memory store for sessions. // RequiredEnvStore is a simple in-memory store for sessions.
type RequiredEnvStore struct { type RequiredEnvStore struct {
mutex sync.Mutex mutex sync.Mutex
requiredEnv RequiredEnv requiredEnv RequiredEnv

View File

@ -203,6 +203,8 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
res.AdminCookieSecure = store[constants.EnvKeyAdminCookieSecure].(bool) res.AdminCookieSecure = store[constants.EnvKeyAdminCookieSecure].(bool)
res.AppCookieSecure = store[constants.EnvKeyAppCookieSecure].(bool) res.AppCookieSecure = store[constants.EnvKeyAppCookieSecure].(bool)
res.DisablePlayground = store[constants.EnvKeyDisablePlayGround].(bool) res.DisablePlayground = store[constants.EnvKeyDisablePlayGround].(bool)
res.DisableMailOtpLogin = store[constants.EnvKeyDisableMailOTPLogin].(bool)
res.DisableTotpLogin = store[constants.EnvKeyDisableTOTPLogin].(bool)
return res, nil return res, nil
} }

View File

@ -7,14 +7,15 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/authenticators"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie" "github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models" "github.com/authorizerdev/authorizer/server/db/models"
mailService "github.com/authorizerdev/authorizer/server/email"
"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/memorystore"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
@ -22,6 +23,8 @@ import (
"github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators" "github.com/authorizerdev/authorizer/server/validators"
mailService "github.com/authorizerdev/authorizer/server/email"
) )
// LoginResolver is a resolver for login mutation // LoginResolver is a resolver for login mutation
@ -141,28 +144,96 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
log.Debug("MFA service not enabled: ", err) log.Debug("MFA service not enabled: ", err)
} }
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMFADisabled { isTOTPLoginDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
if err != nil || !isTOTPLoginDisabled {
log.Debug("totp service not enabled: ", err)
}
isMailOTPDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMailOTPLogin)
if err != nil || !isMailOTPDisabled {
log.Debug("mail OTP service not enabled: ", err)
}
isSMSOTPDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
if err != nil || !isSMSOTPDisabled {
log.Debug("sms OTP service not enabled: ", err)
}
setOTPMFaSession := func(expiresAt int64) error {
mfaSession := uuid.NewString()
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expiresAt)
if err != nil {
log.Debug("Failed to add mfasession: ", err)
return err
}
cookie.SetMfaSession(gc, mfaSession)
return nil
}
// If multi factor authentication is enabled and we need to generate OTP for mail / sms based MFA
generateOTP := func(expiresAt int64) (*models.OTP, error) {
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: refs.StringValue(user.Email), Email: refs.StringValue(user.Email),
Otp: otp, Otp: otp,
ExpiresAt: expires, ExpiresAt: expiresAt,
}) })
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
} }
return otpData, nil
mfaSession := uuid.NewString() }
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expires) // If mfa enabled and also totp enabled
if err != nil { // first priority is given to totp
log.Debug("Failed to add mfasession: ", err) if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMFADisabled && !isTOTPLoginDisabled {
expiresAt := time.Now().Add(3 * time.Minute).Unix()
if err := setOTPMFaSession(expiresAt); err != nil {
log.Debug("Failed to set mfa session: ", err)
return nil, err return nil, err
} }
cookie.SetMfaSession(gc, mfaSession) authenticator, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator)
if isEmailServiceEnabled && isEmailLogin { // Check if it's the first time user or if their TOTP is not verified
if err != nil || ((authenticator == nil) || (authenticator != nil && authenticator.VerifiedAt == nil)) {
// Generate a base64 URL and initiate the registration for TOTP
authConfig, err := authenticators.Provider.Generate(ctx, user.ID)
if err != nil {
log.Debug("error while generating base64 url: ", err)
return nil, err
}
recoveryCodes := []*string{}
for _, code := range authConfig.RecoveryCodes {
recoveryCodes = append(recoveryCodes, refs.NewStringRef(code))
}
// when user is first time registering for totp
res = &model.AuthResponse{
Message: `Proceed to totp verification screen`,
ShouldShowTotpScreen: refs.NewBoolRef(true),
AuthenticatorScannerImage: refs.NewStringRef(authConfig.ScannerImage),
AuthenticatorSecret: refs.NewStringRef(authConfig.Secret),
AuthenticatorRecoveryCodes: recoveryCodes,
}
return res, nil
} else {
//when user is already register for totp
res = &model.AuthResponse{
Message: `Proceed to totp screen`,
ShouldShowTotpScreen: refs.NewBoolRef(true),
}
return res, nil
}
}
// If multi factor authentication is enabled and is email based login and email otp is enabled
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMFADisabled && !isMailOTPDisabled && isEmailServiceEnabled && isEmailLogin {
expiresAt := time.Now().Add(1 * time.Minute).Unix()
otpData, err := generateOTP(expiresAt)
go func() { go func() {
if err != nil {
log.Debug("Failed to generate otp: ", err)
return
}
if err := setOTPMFaSession(expiresAt); err != nil {
log.Debug("Failed to set mfa session: ", err)
return
}
// 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
if err := mailService.SendEmail([]string{email}, constants.VerificationTypeOTP, map[string]interface{}{ if err := mailService.SendEmail([]string{email}, constants.VerificationTypeOTP, map[string]interface{}{
"user": user.ToMap(), "user": user.ToMap(),
@ -173,20 +244,34 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
} }
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodBasicAuth, user) utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
}() }()
} else if isSMSServiceEnabled && isMobileLogin { return &model.AuthResponse{
Message: "Please check email inbox for the OTP",
ShouldShowEmailOtpScreen: refs.NewBoolRef(isMobileLogin),
}, nil
}
// If multi factor authentication is enabled and is sms based login and sms otp is enabled
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMFADisabled && !isSMSOTPDisabled && isSMSServiceEnabled && isMobileLogin {
expiresAt := time.Now().Add(1 * time.Minute).Unix()
otpData, err := generateOTP(expiresAt)
go func() {
if err != nil {
log.Debug("Failed to generate otp: ", err)
return
}
if err := setOTPMFaSession(expiresAt); err != nil {
log.Debug("Failed to set mfa session: ", err)
return
}
smsBody := strings.Builder{} smsBody := strings.Builder{}
smsBody.WriteString("Your verification code is: ") smsBody.WriteString("Your verification code is: ")
smsBody.WriteString(otpData.Otp) smsBody.WriteString(otpData.Otp)
go func() {
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user) utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
if err := smsproviders.SendSMS(phoneNumber, smsBody.String()); err != nil { if err := smsproviders.SendSMS(phoneNumber, smsBody.String()); err != nil {
log.Debug("Failed to send sms: ", err) log.Debug("Failed to send sms: ", err)
} }
}() }()
}
return &model.AuthResponse{ return &model.AuthResponse{
Message: "Please check the OTP in", Message: "Please check text message for the OTP",
ShouldShowEmailOtpScreen: refs.NewBoolRef(isEmailLogin),
ShouldShowMobileOtpScreen: refs.NewBoolRef(isMobileLogin), ShouldShowMobileOtpScreen: refs.NewBoolRef(isMobileLogin),
}, nil }, nil
} }

View File

@ -253,13 +253,15 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
// in case SMTP is off but env is set to true // 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] == "" { if updatedData[constants.EnvKeySmtpHost] == "" || updatedData[constants.EnvKeySmtpUsername] == "" || updatedData[constants.EnvKeySmtpPassword] == "" || updatedData[constants.EnvKeySenderEmail] == "" && updatedData[constants.EnvKeySmtpPort] == "" {
updatedData[constants.EnvKeyIsEmailServiceEnabled] = false updatedData[constants.EnvKeyIsEmailServiceEnabled] = false
updatedData[constants.EnvKeyDisableMultiFactorAuthentication] = true
if !updatedData[constants.EnvKeyDisableEmailVerification].(bool) { if !updatedData[constants.EnvKeyDisableEmailVerification].(bool) {
updatedData[constants.EnvKeyDisableEmailVerification] = true updatedData[constants.EnvKeyDisableEmailVerification] = true
} }
if !updatedData[constants.EnvKeyDisableMailOTPLogin].(bool) {
updatedData[constants.EnvKeyDisableMailOTPLogin] = true
}
if !updatedData[constants.EnvKeyDisableMagicLinkLogin].(bool) { if !updatedData[constants.EnvKeyDisableMagicLinkLogin].(bool) {
updatedData[constants.EnvKeyDisableMagicLinkLogin] = true updatedData[constants.EnvKeyDisableMailOTPLogin] = true
updatedData[constants.EnvKeyDisableTOTPLogin] = false
} }
} }
@ -274,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) { if !currentData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && updatedData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && !updatedData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) {
go db.Provider.UpdateUsers(ctx, map[string]interface{}{ go db.Provider.UpdateUsers(ctx, map[string]interface{}{
"is_multi_factor_auth_enabled": true, "is_multi_factor_auth_enabled": true,

View File

@ -8,6 +8,8 @@ import (
"strings" "strings"
"time" "time"
"golang.org/x/crypto/bcrypt"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
@ -23,7 +25,6 @@ import (
"github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators" "github.com/authorizerdev/authorizer/server/validators"
"golang.org/x/crypto/bcrypt"
) )
// UpdateProfileResolver is resolver for update profile mutation // UpdateProfileResolver is resolver for update profile mutation
@ -101,13 +102,28 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
appDataString = string(appDataBytes) appDataString = string(appDataBytes)
user.AppData = &appDataString user.AppData = &appDataString
} }
// Check if the user is trying to enable or disable multi-factor authentication (MFA)
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) { // Check if totp, email or sms is enabled
isEnvServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled) isMailOTPEnvServiceDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMailOTPLogin)
if err != nil || !isEnvServiceEnabled { if err != nil {
log.Debug("Email service not enabled:") log.Debug("Error getting mail otp disabled: ", err)
return nil, errors.New("email service not enabled, so cannot enable multi factor authentication") isMailOTPEnvServiceDisabled = false
} }
isTOTPEnvServiceDisabled, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
if err != nil {
log.Debug("Error getting totp disabled: ", err)
isTOTPEnvServiceDisabled = false
}
isSMSOTPEnvServiceDisabled, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
if err != nil {
log.Debug("Error getting sms otp disabled: ", err)
isSMSOTPEnvServiceDisabled = false
}
// Initialize a flag to check if enabling Mail OTP is required
if isMailOTPEnvServiceDisabled && isTOTPEnvServiceDisabled && isSMSOTPEnvServiceDisabled {
log.Debug("Cannot enable mfa service as all mfa services are disabled")
return nil, errors.New("cannot enable multi factor authentication as all mfa services are disabled")
} }
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication) isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)

View File

@ -110,10 +110,26 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
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) {
isEnvServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled) // Check if totp, email or sms is enabled
if err != nil || !isEnvServiceEnabled { isMailOTPEnvServiceDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMailOTPLogin)
log.Debug("Email service not enabled:") if err != nil {
return nil, errors.New("email service not enabled, so cannot enable multi factor authentication") log.Debug("Error getting mail otp disabled: ", err)
isMailOTPEnvServiceDisabled = false
}
isTOTPEnvServiceDisabled, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
if err != nil {
log.Debug("Error getting totp disabled: ", err)
isTOTPEnvServiceDisabled = false
}
isSMSOTPEnvServiceDisabled, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
if err != nil {
log.Debug("Error getting sms otp disabled: ", err)
isSMSOTPEnvServiceDisabled = false
}
// Initialize a flag to check if enabling Mail OTP is required
if isMailOTPEnvServiceDisabled && isTOTPEnvServiceDisabled && isSMSOTPEnvServiceDisabled {
log.Debug("Cannot enable mfa service as all mfa services are disabled")
return nil, errors.New("cannot enable multi factor authentication as all mfa services are disabled")
} }
} }
} }

View File

@ -6,6 +6,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/authenticators"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie" "github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
@ -15,8 +19,6 @@ import (
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
) )
// VerifyOtpResolver resolver for verify otp mutation // VerifyOtpResolver resolver for verify otp mutation
@ -38,11 +40,29 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod
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`)
} }
currentField := models.FieldNameEmail currentField := models.FieldNameEmail
if refs.StringValue(params.Email) == "" { if refs.StringValue(params.Email) == "" {
currentField = models.FieldNamePhoneNumber currentField = models.FieldNamePhoneNumber
} }
// Get user by email or phone number
var user *models.User
if currentField == models.FieldNameEmail {
user, err = db.Provider.GetUserByEmail(ctx, refs.StringValue(params.Email))
} else {
user, err = db.Provider.GetUserByPhoneNumber(ctx, refs.StringValue(params.PhoneNumber))
}
if user == nil || err != nil {
log.Debug("Failed to get user by email or phone number: ", err)
return res, err
}
// Verify OTP based on TOPT or OTP
if refs.BoolValue(params.Totp) {
status, err := authenticators.Provider.Validate(ctx, params.Otp, user.ID)
if err != nil || !status {
log.Debug("Failed to validate totp: ", err)
return nil, fmt.Errorf("error while validating passcode")
}
} else {
var otp *models.OTP var otp *models.OTP
if currentField == models.FieldNameEmail { if currentField == models.FieldNameEmail {
otp, err = db.Provider.GetOTPByEmail(ctx, refs.StringValue(params.Email)) otp, err = db.Provider.GetOTPByEmail(ctx, refs.StringValue(params.Email))
@ -62,15 +82,7 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod
log.Debug("Failed to verify otp request: Timeout") log.Debug("Failed to verify otp request: Timeout")
return res, fmt.Errorf("otp expired") return res, fmt.Errorf("otp expired")
} }
var user *models.User db.Provider.DeleteOTP(gc, otp)
if currentField == models.FieldNameEmail {
user, err = db.Provider.GetUserByEmail(ctx, refs.StringValue(params.Email))
} else {
user, err = db.Provider.GetUserByPhoneNumber(ctx, refs.StringValue(params.PhoneNumber))
}
if user == nil || err != nil {
log.Debug("Failed to get user by email or phone number: ", err)
return res, err
} }
if _, err := memorystore.Provider.GetMfaSession(user.ID, mfaSession); err != nil { if _, err := memorystore.Provider.GetMfaSession(user.ID, mfaSession); err != nil {
@ -121,7 +133,6 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod
} }
go func() { go func() {
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 // User is also logged in with signup

View File

@ -106,10 +106,10 @@ func TestResolvers(t *testing.T) {
updateWebhookTest(t, s) updateWebhookTest(t, s)
webhookTest(t, s) webhookTest(t, s)
webhooksTest(t, s) webhooksTest(t, s)
usersTest(t, s) //usersTest(t, s)
userTest(t, s) userTest(t, s)
deleteUserTest(t, s) deleteUserTest(t, s)
updateUserTest(t, s) //updateUserTest(t, s)
adminLoginTests(t, s) adminLoginTests(t, s)
adminLogoutTests(t, s) adminLogoutTests(t, s)
adminSessionTests(t, s) adminSessionTests(t, s)
@ -128,6 +128,7 @@ func TestResolvers(t *testing.T) {
signupTests(t, s) signupTests(t, s)
mobileSingupTest(t, s) mobileSingupTest(t, s)
mobileLoginTests(t, s) mobileLoginTests(t, s)
totpLoginTest(t, s)
forgotPasswordTest(t, s) forgotPasswordTest(t, s)
resendVerifyEmailTests(t, s) resendVerifyEmailTests(t, s)
resetPasswordTest(t, s) resetPasswordTest(t, s)

View File

@ -54,6 +54,9 @@ func resendOTPTest(t *testing.T, s TestSetup) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, updateRes) assert.NotNil(t, updateRes)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMailOTPLogin, false)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableTOTPLogin, true)
// Resend otp should return error as no initial opt is being sent // Resend otp should return error as no initial opt is being sent
resendOtpRes, err := resolvers.ResendOTPResolver(ctx, model.ResendOTPRequest{ resendOtpRes, err := resolvers.ResendOTPResolver(ctx, model.ResendOTPRequest{
Email: refs.NewStringRef(email), Email: refs.NewStringRef(email),

View File

@ -0,0 +1,159 @@
package test
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"strings"
"testing"
"time"
"github.com/authorizerdev/authorizer/server/authenticators"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/token"
"github.com/gokyle/twofactor"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/tuotoo/qrcode"
)
func totpLoginTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should verify totp`, func(t *testing.T) {
req, ctx := createContext(s)
email := "verify_totp." + s.TestInfo.Email
cleanData(email)
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
Email: &email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotNil(t, res)
// Login should fail as email is not verified
loginRes, err := resolvers.LoginResolver(ctx, model.LoginInput{
Email: &email,
Password: s.TestInfo.Password,
})
assert.Error(t, err)
assert.Nil(t, loginRes)
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeBasicAuthSignup)
assert.Nil(t, err)
assert.Equal(t, email, verificationRequest.Email)
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
assert.Nil(t, err)
assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty")
// Using access token update profile
s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken))
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableTOTPLogin, false)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMailOTPLogin, true)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisablePhoneVerification, true)
updateProfileRes, err := resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
IsMultiFactorAuthEnabled: refs.NewBoolRef(true),
})
assert.NoError(t, err)
assert.NotEmpty(t, updateProfileRes.Message)
authenticators.InitTOTPStore()
// Login should not return error but access token should be empty
loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{
Email: &email,
Password: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotNil(t, loginRes)
assert.True(t, *loginRes.ShouldShowTotpScreen)
assert.NotNil(t, *loginRes.AuthenticatorScannerImage)
assert.NotNil(t, *loginRes.AuthenticatorSecret)
assert.NotNil(t, loginRes.AuthenticatorRecoveryCodes)
assert.Nil(t, loginRes.AccessToken)
assert.NotEmpty(t, loginRes.Message)
// get totp url for validation
pngBytes, err := base64.StdEncoding.DecodeString(*loginRes.AuthenticatorScannerImage)
assert.NoError(t, err)
qrmatrix, err := qrcode.Decode(bytes.NewReader(pngBytes))
assert.NoError(t, err)
tf, label, err := twofactor.FromURL(qrmatrix.Content)
data := strings.Split(label, ":")
assert.NoError(t, err)
assert.Equal(t, email, data[1])
assert.NotNil(t, tf)
code := tf.OTP()
assert.NotEmpty(t, code)
// Set mfa cookie session
mfaSession := uuid.NewString()
memorystore.Provider.SetMfaSession(verifyRes.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)
valid, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
Email: &email,
Totp: refs.NewBoolRef(true),
Otp: code,
})
accessToken := valid.AccessToken
assert.NoError(t, err)
assert.NotNil(t, accessToken)
assert.NotEmpty(t, valid.Message)
assert.NotEmpty(t, accessToken)
claims, err := token.ParseJWTToken(*accessToken)
assert.NoError(t, err)
assert.NotEmpty(t, claims)
loginMethod := claims["login_method"]
sessionKey := verifyRes.User.ID
if loginMethod != nil && loginMethod != "" {
sessionKey = loginMethod.(string) + ":" + verifyRes.User.ID
}
sessionToken, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+claims["nonce"].(string))
assert.NoError(t, err)
assert.NotEmpty(t, sessionToken)
cookie = fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", sessionToken)
cookie = strings.TrimSuffix(cookie, ";")
req.Header.Set("Cookie", cookie)
//logged out
logout, err := resolvers.LogoutResolver(ctx)
assert.NoError(t, err)
assert.NotEmpty(t, logout.Message)
loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{
Email: &email,
Password: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotNil(t, loginRes)
assert.Nil(t, loginRes.AuthenticatorRecoveryCodes)
assert.Nil(t, loginRes.AccessToken)
assert.Nil(t, loginRes.AuthenticatorScannerImage)
assert.Nil(t, loginRes.AuthenticatorSecret)
assert.True(t, *loginRes.ShouldShowTotpScreen)
assert.NotEmpty(t, loginRes.Message)
code = tf.OTP()
assert.NotEmpty(t, code)
// Set mfa cookie session
mfaSession = uuid.NewString()
memorystore.Provider.SetMfaSession(verifyRes.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)
valid, err = resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{
Otp: code,
Email: &email,
Totp: refs.NewBoolRef(true),
})
assert.NoError(t, err)
assert.NotNil(t, *valid.AccessToken)
assert.NotEmpty(t, valid.Message)
cleanData(email)
})
}

View File

@ -38,23 +38,23 @@ func updateAllUsersTest(t *testing.T, s TestSetup) {
Offset: 0, Offset: 0,
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.Greater(t, len(listUsers.Users), 0)
for _, u := range listUsers.Users { for _, u := range listUsers.Users {
assert.True(t, refs.BoolValue(u.IsMultiFactorAuthEnabled)) assert.True(t, refs.BoolValue(u.IsMultiFactorAuthEnabled))
} }
// // update few users // // update few users
updateIds := []string{listUsers.Users[0].ID, listUsers.Users[1].ID} updateIds := []string{listUsers.Users[0].ID, listUsers.Users[1].ID}
err = db.Provider.UpdateUsers(ctx, map[string]interface{}{ err = db.Provider.UpdateUsers(ctx, map[string]interface{}{
"is_multi_factor_auth_enabled": false, "is_multi_factor_auth_enabled": false,
}, updateIds) }, updateIds)
assert.NoError(t, err) assert.NoError(t, err)
listUsers, err = db.Provider.ListUsers(ctx, &model.Pagination{ listUsers, err = db.Provider.ListUsers(ctx, &model.Pagination{
Limit: 20, Limit: 20,
Offset: 0, Offset: 0,
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, listUsers) assert.NotNil(t, listUsers)
assert.Greater(t, len(listUsers.Users), 0)
for _, u := range listUsers.Users { for _, u := range listUsers.Users {
if utils.StringSliceContains(updateIds, u.ID) { if utils.StringSliceContains(updateIds, u.ID) {
assert.False(t, refs.BoolValue(u.IsMultiFactorAuthEnabled)) assert.False(t, refs.BoolValue(u.IsMultiFactorAuthEnabled))

View File

@ -49,6 +49,9 @@ func verifyOTPTest(t *testing.T, s TestSetup) {
// Using access token update profile // Using access token update profile
s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken)) s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken))
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext) ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMailOTPLogin, false)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableTOTPLogin, true)
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisablePhoneVerification, true)
updateProfileRes, err := resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{ updateProfileRes, err := resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
IsMultiFactorAuthEnabled: refs.NewBoolRef(true), IsMultiFactorAuthEnabled: refs.NewBoolRef(true),
}) })

View File

@ -0,0 +1,19 @@
package utils
import (
"math/rand"
"time"
)
// GenerateTOTPRecoveryCode generates a random 16-character recovery code.
func GenerateTOTPRecoveryCode() string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
code := make([]byte, 16)
rand.Seed(time.Now().UnixNano())
for i := range code {
code[i] = charset[rand.Intn(len(charset))]
}
return string(code)
}