Compare commits

...

996 Commits

Author SHA1 Message Date
46e93aeefd log-inside-post
All checks were successful
deploy / deploy (push) Successful in 1m31s
2024-06-06 14:50:29 +03:00
48e1dbf8aa get-id-debug
All checks were successful
deploy / deploy (push) Successful in 1m29s
2024-06-06 07:51:45 +03:00
55121656a8 get-id
All checks were successful
deploy / deploy (push) Successful in 1m28s
2024-06-05 23:47:41 +03:00
e9f54a74f0 get-id
All checks were successful
deploy / deploy (push) Successful in 1m26s
2024-06-05 23:40:09 +03:00
bfa192d21e log-author-id
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 23:20:37 +03:00
800fc4f8d8 fixed-redis-intfix4
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 22:17:18 +03:00
86453d483e fixed-redis-intfix2-вуигп
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 22:15:03 +03:00
7d5d31692f fixed-redis-intfix2
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 22:10:43 +03:00
5745a9b3c4 fixed-redis-intfix
Some checks failed
deploy / deploy (push) Failing after 1m4s
2024-06-05 22:07:06 +03:00
641c5bc248 fixed-redis-debug
All checks were successful
deploy / deploy (push) Successful in 1m31s
2024-06-05 22:01:24 +03:00
085faf73e7 fixed-redis-cache-5
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 21:53:59 +03:00
3197154fec fixed-redis-cache-3
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 21:32:10 +03:00
b8f5e9ebb9 fixed-redis-cache-2
Some checks failed
deploy / deploy (push) Failing after 1m3s
2024-06-05 21:27:51 +03:00
76f976937a fixed-redis-cache
Some checks failed
deploy / deploy (push) Failing after 1m3s
2024-06-05 21:20:36 +03:00
693f252ae9 redis-key 2024-05-30 18:34:25 +03:00
12af7fc617 reimplement-following-data-proxy-4
All checks were successful
deploy / deploy (push) Successful in 1m29s
2024-05-21 03:38:56 +03:00
32eaff5c5d reimplement-following-data-proxy-3
Some checks failed
deploy / deploy (push) Failing after 29s
2024-05-21 03:37:14 +03:00
0b823ffc33 reimplement-following-data-proxy-2
Some checks failed
deploy / deploy (push) Failing after 1m3s
2024-05-21 03:31:06 +03:00
42017035fc reimplement-following-data-proxy
Some checks failed
deploy / deploy (push) Failing after 1m7s
2024-05-21 03:25:15 +03:00
c18209418f postmerge
All checks were successful
deploy / deploy (push) Successful in 1m34s
2024-05-21 03:10:43 +03:00
c8ba8c1453 yml-fix
Some checks failed
deploy / deploy (push) Failing after 5s
2024-05-21 03:03:59 +03:00
04a8e292c0 Merge branch 'main' into discours
Some checks failed
deploy / deploy (push) Failing after 6s
2024-05-21 03:00:07 +03:00
Lakhan Samani
9336d3e003 Merge pull request #455 from testwill/ioutil
chore: remove refs to deprecated io/ioutil
2024-05-20 18:23:40 +05:30
guoguangwu
c6f19a82a4 chore: remove refs to deprecated io/ioutil
Signed-off-by: guoguangwu <guoguangwug@gmail.com>
2024-05-18 15:02:45 +08:00
Lakhan Samani
28390ebbe4 fix: social login state 2024-05-08 09:56:06 +05:30
Lakhan Samani
5add964449 Merge pull request #453 from authorizerdev/feat-add-roblox-login
Add roblox login feature
2024-05-01 18:17:17 +05:30
Lakhan Samani
4eb8ca0d65 update roblox ui 2024-05-01 17:40:11 +05:30
Lakhan Samani
d924e36786 feat: fix roblox image 2024-05-01 17:04:33 +05:30
Lakhan Samani
d0e6392f1a fix roblox oauth 2024-05-01 16:36:21 +05:30
Lakhan Samani
b931652769 Add roblox login feature 2024-04-30 13:13:27 +05:30
fb65fcbad5 deploy
Some checks failed
deploy / deploy (push) Failing after 6s
2024-04-15 14:12:12 +03:00
79d2d52fc0 deploy
Some checks failed
deploy / deploy (push) Failing after 5s
2024-04-15 09:33:44 +03:00
2a766d587f branch 2024-04-15 09:32:16 +03:00
5c33b159dc Merge remote-tracking branch 'hub/main' into discours 2024-04-15 09:31:44 +03:00
Stepan Vladovskiy
ca0118df5e debug: no force in push to staging
All checks were successful
deploy / deploy (push) Successful in 29s
2024-04-11 17:16:39 -03:00
Stepan Vladovskiy
8eb9650142 feat: merge with mailgun
All checks were successful
deploy / deploy (push) Successful in 1m32s
2024-04-11 17:14:25 -03:00
Stepan Vladovskiy
cfaf6c49b4 feat: CI mailgun->v2 and dev->staging
All checks were successful
deploy / deploy (push) Successful in 50s
2024-04-11 17:10:55 -03:00
Stepan Vladovskiy
4990a77786 feat: CI mailgun->v2 and dev->staging
Some checks failed
deploy / deploy (push) Failing after 12s
2024-04-11 17:09:38 -03:00
Lakhan Samani
82f639757f Merge pull request #450 from authorizerdev/fix-user-verification
fix: user verification
2024-04-02 15:39:48 +05:30
Lakhan Samani
28b574c827 Update tests 2024-04-02 15:25:11 +05:30
Lakhan Samani
67f866a787 fix: user verification 2024-03-28 13:57:47 +05:30
Stepan Vladovskiy
2420297c20 feat: branch dev with CI to deploy dev in staging.discour.io
Some checks failed
deploy / deploy (push) Failing after 1m39s
2024-03-18 00:12:17 -03:00
5616186be7 cache-fix 2024-03-12 15:51:49 +03:00
4351f5cd32 type-fix-8 2024-03-02 14:51:37 +03:00
29026ed6fb type-fix-7 2024-03-02 14:44:34 +03:00
f244330401 type-fix-6 2024-03-02 14:40:40 +03:00
132f965c8b type-fix-5 2024-03-02 14:35:40 +03:00
8deba4849d type-fix-4 2024-03-02 14:22:27 +03:00
3a3407f85e type-fix-3 2024-03-02 14:14:54 +03:00
7a1df30325 type-fix 2024-03-02 14:06:20 +03:00
d9fd6c2b36 type-fix 2024-03-02 13:56:57 +03:00
11472d5b06 logs-fix-14 2024-03-02 13:48:10 +03:00
3502d8b3d1 logs-fix-14 2024-03-02 13:46:58 +03:00
7c949417c1 logs-fix-13 2024-03-02 13:41:23 +03:00
09e4daf66e logs-fix-12 2024-03-02 13:34:00 +03:00
32f903f40a logs-fix-11 2024-03-02 13:28:20 +03:00
348a9426cc logs-fix-10 2024-03-02 13:22:19 +03:00
9ddbfb5df7 logs-fix-9 2024-03-02 13:18:22 +03:00
9fa248ca41 logs-fix-9 2024-03-02 13:17:57 +03:00
0b0ab12bf0 logs-fix-8 2024-03-02 13:13:20 +03:00
be58bb8c6d logs-fix-7 2024-03-02 13:08:48 +03:00
cdad58bd02 logs-fix-6 2024-03-02 12:58:39 +03:00
934f433dd2 logs-fix-5 2024-03-02 12:55:46 +03:00
d6fec9121c logs-fix-4 2024-03-02 12:51:26 +03:00
f9e1316145 logs-fix-3 2024-03-02 12:47:06 +03:00
362d410e37 logs-fix-2 2024-03-02 12:35:12 +03:00
78930dc8f3 logs-fix 2024-03-02 11:56:24 +03:00
baeb5a68ea appdata-debug-4 2024-03-02 10:19:56 +03:00
040f7ead7e appdata-debug-3 2024-03-02 10:12:05 +03:00
3246d32a23 appdata-debug-2 2024-03-02 10:08:38 +03:00
ce5b09953e appdata-debug 2024-03-02 09:56:48 +03:00
31079f2628 redis-provider-fix
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 11:51:20 +03:00
8fca4cf4c0 appdata-patch
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 10:40:39 +03:00
94917e7735 login-patched
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 09:13:56 +03:00
0f9b95cafe core-api-redis
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 00:33:24 +03:00
871c65b9dd app-data-patch
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 00:29:46 +03:00
2487077adc Merge branch 'feature/discoursio-auth' into mailgun
All checks were successful
deploy / deploy (push) Successful in 5s
2024-02-17 14:23:28 +03:00
7c03b383f1 Merge branch 'mailgun' of https://dev.discours.io/discours.io/authorizer into mailgun 2024-02-17 14:23:23 +03:00
e0db7c1051 ci 2024-02-17 14:21:45 +03:00
19f5e7e174 forgot-resolver-patch
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-08 19:18:31 +03:00
02deafa879 magic-lnk-login-only
All checks were successful
deploy / deploy (push) Successful in 1m35s
2024-02-08 19:04:24 +03:00
42efbc232b check-not-basic
All checks were successful
deploy / deploy (push) Successful in 1m35s
2024-02-08 18:54:44 +03:00
7bc9fca4c4 magic
All checks were successful
deploy / deploy (push) Successful in 1m37s
2024-02-08 18:05:08 +03:00
b20f5986c1 is-registered-debug
All checks were successful
deploy / deploy (push) Successful in 1m33s
2024-02-06 12:25:40 +03:00
a66393e754 resolver-fix
All checks were successful
deploy / deploy (push) Successful in 1m32s
2024-02-06 12:20:01 +03:00
e963f325f8 is-registered-hotfix-4
All checks were successful
deploy / deploy (push) Successful in 1m31s
2024-02-06 12:12:34 +03:00
570436e6e8 schema-regen
All checks were successful
deploy / deploy (push) Successful in 1m25s
2024-02-06 12:03:42 +03:00
c4472340ef is-registered-hotfix-3
All checks were successful
deploy / deploy (push) Successful in 32s
2024-02-06 11:38:45 +03:00
7a6ae24aaa is-registered-hotfix-2
All checks were successful
deploy / deploy (push) Successful in 1m29s
2024-02-06 11:35:48 +03:00
db25ceb0b5 is-registered-hotfix
All checks were successful
deploy / deploy (push) Successful in 1m29s
2024-02-06 11:34:00 +03:00
40b8e020a3 fixfix
All checks were successful
deploy / deploy (push) Successful in 1m30s
2024-02-02 16:06:57 +03:00
4b249d643f is-registered-fix-2
Some checks failed
deploy / deploy (push) Failing after 1m2s
2024-02-01 16:37:49 +03:00
41f99d1199 Merge branch 'mailgun' of https://dev.discours.io/discours.io/authorizer into mailgun
All checks were successful
deploy / deploy (push) Successful in 1m30s
2024-02-01 16:22:28 +03:00
83bb18ae79 is-registered-fix 2024-02-01 16:21:53 +03:00
to
416640ac13 Update server/graph/schema.graphqls
All checks were successful
deploy / deploy (push) Successful in 1m28s
2024-02-01 10:35:11 +00:00
Stepan Vladovskii
853f8c44bb feat: add CI pipline to mailgun brunch with check is this branch is mailgun
All checks were successful
deploy / deploy (push) Successful in 1m33s
2024-01-28 19:33:27 -03:00
bdcf2c39f7 logs-fix-4 2024-01-22 15:02:22 +03:00
c53ada95eb logs-fix-3 2024-01-22 14:49:58 +03:00
07a26991e9 logs-fix-2 2024-01-22 14:32:27 +03:00
c8413665ae logs-fix 2024-01-22 14:29:32 +03:00
69a87896e9 Merge branch 'main' of https://github.com/authorizerdev/authorizer into mailgun 2024-01-22 13:48:09 +03:00
Lakhan Samani
27b51ad369 Merge pull request #436 from team-scaletech/feat/totp_for_signup
Feat/totp for signup
2024-01-19 21:26:35 +05:30
Lakhan Samani
747c82f1b9 Merge pull request #439 from team-scaletech/fix/role-deletion
role deletion
2024-01-19 21:25:36 +05:30
Lakhan Samani
b1bfaf6688 Merge pull request #443 from cosark/patch-1
Added one-click deployment option to readme table
2024-01-19 21:23:41 +05:30
034d80303f Merge remote-tracking branch 'hub/main' into mailgun 2024-01-18 18:50:16 +03:00
cosark
a328121aa3 Added one-click deployment option to readme table
Adding RepoCloud.io as a 1-click deployment options for Authorizer instances. Provides diversity and a user-friendly deployment option.
2024-01-12 02:15:04 -07:00
lemonScaletech
40a0a2fbcc feat:
* added chunks of 1000 for deletion of role
2024-01-10 12:56:30 +05:30
lemonScaletech
e52164665f Merge branch 'main' into fix/role-deletion 2024-01-10 12:30:47 +05:30
Anand Kumar Panigrahi
a63d00b0c8 Merge branch 'authorizerdev:main' into main 2024-01-10 12:23:03 +05:30
Lakhan Samani
0bce901749 remove debug logs 2024-01-08 14:28:23 +05:30
Lakhan Samani
e5fbaa26e1 fix: pkce flow for oauth login 2024-01-08 14:21:24 +05:30
bb62b4adfd templates-fix 2024-01-05 20:35:58 +03:00
d47b39bb1d link-fix 2024-01-05 19:43:45 +03:00
0aa1c2532b debug-link-2 2024-01-05 19:29:50 +03:00
0ab26c19b6 debug-link 2024-01-05 19:27:59 +03:00
b8aade7dc0 sdk-fix 2024-01-05 19:17:55 +03:00
6e0ab799b8 bytes-fix 2024-01-05 18:56:13 +03:00
2ccc2ae4eb mailgun-debug 2024-01-05 18:51:39 +03:00
95807ae319 monkey-fix 2024-01-05 18:45:13 +03:00
e62f356a79 debug-mailgun 2024-01-05 18:26:58 +03:00
0331d34afc mailgun-response-debug 2024-01-05 18:04:19 +03:00
5af71dfc94 template-names-fix 2024-01-05 16:44:37 +03:00
dd4b41674a is-registered-1 2024-01-05 14:58:16 +03:00
58a91814ae minor-fix 2024-01-05 14:17:56 +03:00
2fba6af769 t 2024-01-05 14:09:02 +03:00
ee454aeabe first-only 2024-01-05 14:00:39 +03:00
896e890421 less-bloat-2 2024-01-05 13:19:46 +03:00
c5aaad0662 less-bloat 2024-01-05 13:15:22 +03:00
68c761a181 fixed-imports 2024-01-05 13:12:13 +03:00
f30881de3d mailgun-rest 2024-01-05 13:06:43 +03:00
534b5624af sha256-fix 2024-01-05 01:41:52 +03:00
be88e231e0 imports-fix 2024-01-04 23:53:20 +03:00
1f3cb1aab9 universal-hashing-sha256 2024-01-04 22:15:22 +03:00
scaletech-milan
cb01dea902 Refactor:
- Remove redundant mail otp check
2024-01-02 12:18:51 +05:30
lemonScaletech
7bcd5a70c3 feat:
* PR suggested changes
2024-01-02 11:50:26 +05:30
Lakhan Samani
3bd3a52d3b Merge branch 'main' of https://github.com/authorizerdev/authorizer 2023-12-30 21:19:54 +05:30
Lakhan Samani
ade676f92c fix: remove access_token & id_token from query string 2023-12-30 21:19:44 +05:30
Lakhan Samani
ca71aba96d Merge pull request #440 from foestauf/feat/discord-provider
feat: Add Discord as Identity Provider
2023-12-30 11:16:14 +05:30
Lakhan Samani
ef2a590608 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2023-12-26 21:10:58 +05:30
Lakhan Samani
59ed4e273f Update react app 2023-12-26 21:10:10 +05:30
Robert McKee
751933d40e feat: Add Discord as Identity Provider 2023-12-23 17:05:02 -05:00
Lakhan Samani
5fac440205 Merge pull request #432 from authorizerdev/fix/forgot-password
feat: add forgot password for mobile login
2023-12-22 01:32:49 +05:30
Lakhan Samani
5ba30ccd12 remove todo 2023-12-22 01:32:12 +05:30
Lakhan Samani
02f7a62918 feat: add testing & ui for forgot password with mobile 2023-12-22 01:26:14 +05:30
scaletech-milan
1172c95a23 Merge branch 'authorizerdev:main' into main 2023-12-15 10:28:20 +05:30
Lakhan Samani
c9c2789097 Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/forgot-password 2023-12-14 22:45:36 +05:30
Lakhan Samani
ed3100c179 fix: allow logout using access token 2023-12-14 22:12:03 +05:30
Anand Kumar Panigrahi
b8c6ee0a6d Merge branch 'authorizerdev:main' into main 2023-12-11 15:12:23 +05:30
Lakhan Samani
0a5357c948 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2023-12-09 02:03:36 +05:30
Lakhan Samani
a002e2faf7 fix: remove forgot password for magic link login 2023-12-09 02:03:27 +05:30
lemonScaletech
d8b9ffe9ce Merge remote-tracking branch 'origin/fix/role-deletion' into fix/role-deletion 2023-12-08 18:22:57 +05:30
lemonScaletech
47f26103b0 test:
* added integration test for role deletion functionality
2023-12-08 18:22:24 +05:30
Anand Kumar Panigrahi
48ada9ab26 Merge branch 'authorizerdev:main' into fix/role-deletion 2023-12-08 17:31:01 +05:30
lemonScaletech
b8c2ab4cf8 refactoring:
* removed extra for loop
* commenting on functions
2023-12-08 10:38:09 +05:30
lemonScaletech
5cb94a7820 fix:
* added logic if role is deleted then also be deleted from user side if role is assigned to that user.
* default role should be subset of roles
2023-12-07 19:33:59 +05:30
scaletech-milan
7e9fac335b Feat:
- Add TOTP MFA for signup
- Test cases for totp signup and verify_email
2023-12-06 15:53:01 +05:30
scaletech-milan
febf4f9b15 Merge branch 'authorizerdev:main' into main 2023-12-06 15:35:25 +05:30
Lakhan Samani
b0f54b181d Merge pull request #435 from siimsams/bugfix-appdata-validation 2023-12-06 07:42:09 +05:30
Siim Sams
df5978fb5a fix unable to update app_data for user 2023-12-05 20:41:39 +02:00
Lakhan Samani
2a0e0da436 Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/forgot-password 2023-12-04 23:35:13 +05:30
Lakhan Samani
e4a8eb3542 fix: signup field 2023-12-04 13:44:39 +05:30
Lakhan Samani
06214f0b1d feat: add switch to enable totp 2023-12-04 13:37:58 +05:30
scaletech-milan
a203b853f2 Merge branch 'authorizerdev:main' into main 2023-12-04 11:48:25 +05:30
Lakhan Samani
109b38e588 Merge pull request #433 from authorizerdev/fix/phone_number_verification
fix: phone_number_verified_at set during signup
2023-12-04 11:30:24 +05:30
Lakhan Samani
e8b99f73c3 fix: phone number verification 2023-12-04 11:28:27 +05:30
Lakhan Samani
3fa892431e fix: phone_number_verified_at set during signup 2023-12-04 09:26:59 +05:30
Lakhan Samani
4b341c0a5d Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/forgot-password 2023-12-03 23:05:39 +05:30
Lakhan Samani
c95db8b07b feat: add forgot password for mobile login 2023-12-03 22:49:40 +05:30
Lakhan Samani
32fcba0f8d Fix/forgot password (#430)
* fix: forgot password shown with magic link login

* fix: forgot password shown with magic link login

* fix is basic auth enabled
2023-12-03 22:27:56 +05:30
Lakhan Samani
5b75521490 fix is basic auth enabled 2023-12-03 22:27:37 +05:30
Lakhan Samani
f70310f04f fix: forgot password shown with magic link login 2023-12-03 11:27:39 +05:30
Lakhan Samani
bea6eb8342 fix: forgot password shown with magic link login 2023-12-03 11:17:34 +05:30
Lakhan Samani
cac67b7915 feat: add totp UI & recovery code (#429) 2023-12-03 09:03:22 +05:30
Lakhan Samani
d7da81d308 fix comment for twitch login 2023-12-02 12:22:27 +05:30
scaletech-milan
e49e315967 Feat: Add oauth2 for twitch (#426)
* fix:
* removed fmt.Println

* Feat:
- Add OAuth for twitch

---------

Co-authored-by: lemonScaletech <anand.panigrahi@scaletech.xyz>
Co-authored-by: Anand Kumar Panigrahi <70533637+lemonScaletech@users.noreply.github.com>
2023-12-02 12:21:53 +05:30
scaletech-milan
fbb4975c02 Merge branch 'authorizerdev:main' into main 2023-12-01 14:11:20 +05:30
Lakhan Samani
7f6ddca3fc fix: totp login 2023-12-01 14:00:01 +05:30
Anand Kumar Panigrahi
e71da3def6 Merge branch 'authorizerdev:main' into main 2023-11-28 11:34:24 +05:30
Lakhan Samani
46d6f86ab0 Remove logs 2023-11-25 11:44:52 +05:30
Lakhan Samani
1890db8f03 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2023-11-25 11:34:15 +05:30
Lakhan Samani
0e96e0b6f0 fix: nil check 2023-11-25 11:34:01 +05:30
Aris Ripandi
bbddf484ed feat: add support for libsql / Turso database (#421) 2023-11-25 09:35:39 +05:30
lemonScaletech
7d4c641297 fix:
* removed fmt.Println
2023-11-24 13:52:17 +05:30
Lakhan Samani
bd343f0b27 fix: disable totp by default 2023-11-23 20:54:03 +05:30
Lakhan Samani
ad8bd64987 fix: remove width from logo for vertical logos 2023-11-23 15:42:17 +05:30
scaletech-milan
de5c18b60f Feat: add screen_hint param in /authorize api for explicit signup redirection (#420)
* Feat:
- Introduce screen_hint param in /authorize for explicit signup redirection

* Feat:
- Declare variable for base path and signup path
- Add social login on signup page

* Refactor:
- Update variable name for screen hint param
2023-11-21 13:08:32 +05:30
Lakhan Samani
fe4c693324 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>
2023-11-16 18:30:54 +05:30
Lakhan Samani
d8cd965004 Merge pull request #415 from testwill/typo
fix: typo
2023-11-16 10:44:12 +05:30
guangwu
fdd41721a8 fix: typo 2023-11-16 11:25:57 +08:00
Lakhan Samani
281714b86d Merge pull request #414 from authorizerdev/fix/apple-login
Fix/apple login
2023-11-09 13:46:38 +05:30
Lakhan Samani
a05f5ce063 fix apple login nil pointer exception 2023-11-09 13:45:29 +05:30
Lakhan Samani
b7627a36a6 Add debug point 2023-11-09 10:49:17 +05:30
Lakhan Samani
85bbd1223e Fix validating session 2023-11-01 18:12:56 +05:30
Lakhan Samani
885a147463 Merge pull request #409 from authorizerdev/fix/use-login-signup-for-mobile
feat: unify email & mobile singup + login
2023-10-26 10:08:30 +05:30
Lakhan Samani
9a6f1a659a Allow empty email 2023-10-26 00:55:10 +05:30
Lakhan Samani
4bddbde280 Update comments 2023-10-22 02:36:10 +05:30
Lakhan Samani
3ed31b0557 feat: unify email & mobile singup + login 2023-10-22 02:33:36 +05:30
Lakhan Samani
4d1fcc3004 Merge pull request #408 from Olatunji-Longe/olatunji-longe/OIDC-config-fix
changed subject_types_supported to array of strings according to OIDC…
2023-10-22 01:01:07 +05:30
Olatunji Longe
99441964b5 Clean up ignored files 2023-10-20 20:19:46 -04:00
Olatunji Longe
be3d8cf69b changed subject_types_supported to array of strings according to OIDC specs 2023-10-20 20:10:10 -04:00
Lakhan Samani
734e54db69 Merge pull request #403 from authorizerdev/fix/upgrade-packages
fix: upgrade packages
2023-10-14 18:08:40 +05:30
Lakhan Samani
a3bda429d0 fix: upgrade packages
- fix app_data for cassandra & scylladb
2023-10-14 18:06:29 +05:30
Lakhan Samani
3fba4e4c28 fix: upgrade packages 2023-10-13 10:49:26 +05:30
Lakhan Samani
2a759de311 fix: upgrade packages 2023-10-13 10:48:51 +05:30
Lakhan Samani
e7c4ee5630 Merge pull request #402 from authorizerdev/fix/profile-access
fix: use session / access_token for profile related queries or mutation
2023-10-13 09:09:46 +05:30
Lakhan Samani
6a4568dcf3 fix: use session / access_token for profile related queries or mutation 2023-10-13 08:11:55 +05:30
Lakhan Samani
e941e4834a Merge pull request #401 from authorizerdev/fix/deactivate-acc
Fix calling deactivate acc
2023-10-12 11:49:11 +05:30
Lakhan Samani
f906fb74af Fix calling deactivate acc 2023-10-12 11:48:37 +05:30
Lakhan Samani
7ced811e6e Merge pull request #399 from authorizerdev/feat/deativate-account
Add api to deactivate user account
2023-10-11 00:22:15 +05:30
Lakhan Samani
c1e1ee13f2 Add webhook event to fe 2023-10-11 00:21:58 +05:30
Lakhan Samani
60de61a74e fix webhook for deactiavtion 2023-10-11 00:20:15 +05:30
Lakhan Samani
843bc022fe Add api to deactivate user account 2023-10-11 00:16:53 +05:30
Lakhan Samani
ad41bcf792 Merge pull request #397 from VishwasShashidhar/main
Support macOS arm64 release binaries
2023-09-30 23:54:33 +05:30
Vishwas Shashidhar
ea2596b9ae support darwin-arm64 builds 2023-09-30 23:23:10 +05:30
Lakhan Samani
c8ccb89a67 Merge pull request #391 from team-scaletech/fix/webhook_bug_389
Fix/webhook bug 389
2023-09-13 17:46:55 +05:30
lemonScaletech
9519b53d4e Merge branch 'main' into fix/webhook_bug_389 2023-09-11 11:57:19 +05:30
Anand Kumar Panigrahi
e7cfaf4fbe Merge branch 'authorizerdev:main' into main 2023-09-11 11:55:27 +05:30
Lakhan Samani
0428488dab Merge pull request #393 from Juneezee/refactor/redundant-nil-check
refactor(server/utils): remove redundant nil check
2023-09-11 11:42:58 +05:30
Eng Zer Jun
f3b672a4cf refactor(server/utils): remove redundant nil check
From the Go specification:

  "3. If the map is nil, the number of iterations is 0." [1]

Therefore, an additional nil check for before the loop is unnecessary.

[1]: https://go.dev/ref/spec#For_range

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2023-09-09 12:57:20 +08:00
Lakhan Samani
1d6f569f92 fix: default value for disable playground 2023-09-09 07:11:13 +05:30
Lakhan Samani
fbdc24f314 Merge pull request #390 from team-scaletech/feat/disable_playground
feat: disable playground for non admin
2023-09-08 21:57:57 +05:30
Anand Kumar Panigrahi
1275644abe Merge branch 'authorizerdev:main' into main 2023-09-06 11:30:06 +05:30
Lakhan Samani
ac6b08c093 Merge pull request #392 from imchairmanm/feat/add_koyeb_button
Add button to deploy to Koyeb
2023-09-03 21:11:46 +05:30
Justin Ellingwood
fe461b675b Add button to deploy to Koyeb 2023-09-01 13:30:44 +01:00
lemonScaletech
77e219d990 fix:
* #389 resolved
* typo fixed
2023-08-29 18:14:48 +05:30
lemonScaletech
3ea13d7e93 refactor:
* added blank line eof .env.sample
2023-08-29 12:19:16 +05:30
lemonScaletech
940a09d172 Merge remote-tracking branch 'origin/feat/disable_playground' into feat/disable_playground
# Conflicts:
#	app/yarn.lock
#	server/constants/env.go
#	server/env/env.go
#	server/env/persist_env.go
#	server/memorystore/providers/redis/store.go
2023-08-29 12:00:24 +05:30
lemonScaletech
3ac6875f87 feat:
* resolved conflicts
2023-08-29 11:55:27 +05:30
lemonScaletech
937506ff64 Merge branch 'main' into feat/disable_playground
# Conflicts:
#	app/yarn.lock
#	dashboard/yarn.lock
#	server/constants/env.go
#	server/env/env.go
#	server/env/persist_env.go
#	server/graph/model/models_gen.go
#	server/memorystore/providers/redis/store.go
2023-08-29 11:44:07 +05:30
lemonScaletech
9795ba9097 feat:
* added disable playground functionality
 * added toggle button for playground in dashboard
2023-08-29 11:36:18 +05:30
vipul patel
11dbe9d97a Merge pull request #1 from authorizerdev/main
Authorizer to foke authorizer
2023-08-29 09:15:53 +05:30
lemonScaletech
c9b8bbc3e1 feat:
* added disable playground functionality
 * added toggle button for playground in dashboard
2023-08-28 19:51:42 +05:30
Lakhan Samani
a124edfaee Add user to validate_session
Resolves #379
2023-08-19 20:45:20 +05:30
Lakhan Samani
5e6b033024 fix microsoft active directory config 2023-08-17 14:20:31 +05:30
Lakhan Samani
171d4e3fff remove unused code 2023-08-14 14:16:54 +05:30
Lakhan Samani
cf96a0087f Fix tests for verifying otp using mfa session 2023-08-14 14:15:52 +05:30
Lakhan Samani
09cfad9c27 Merge pull request #382 from authorizerdev/feat-add-field-for-app-data
Add app_data
2023-08-14 12:05:58 +05:30
Lakhan Samani
35e563ab3b Add app_data 2023-08-14 12:01:37 +05:30
Lakhan Samani
e625ed9633 allow common tenant for microsoft 2023-08-03 14:43:27 +05:30
Lakhan Samani
a042c202a0 fix microsoft active directory config 2023-08-03 13:29:07 +05:30
Lakhan Samani
7a76b783b1 Merge pull request #372 from catusax/main
feat: add mfa session to secure otp login
2023-08-03 12:34:39 +05:30
Lakhan Samani
e5400bc7bd fix microsoft active directory config 2023-08-03 12:33:20 +05:30
Lakhan Samani
a8503666e3 fix: add events for signup 2023-08-02 10:02:41 +05:30
Lakhan Samani
b028be3cbc Merge pull request #377 from authorizerdev/fix-webhook-test-endpoint
fix: test webhook endpoint mutation
2023-08-02 00:04:55 +05:30
Lakhan Samani
9a8d20b698 fix: test webhook endpoint mutation
Resolves #376
2023-08-02 00:04:07 +05:30
Lakhan Samani
fab3c2f87e Merge pull request #375 from authorizerdev/fix-db-refs
Fix db refs
2023-08-01 23:38:00 +05:30
Lakhan Samani
3fefcfcd9a fix: refs for dynamo db 2023-08-01 17:37:34 +05:30
Lakhan Samani
c2defdbaac fix: refs for cassandra db 2023-08-01 16:09:17 +05:30
Lakhan Samani
15a4be5431 fix: refs for db provider and few utils 2023-07-31 16:42:11 +05:30
Lakhan Samani
146561bacb fix: allow multi tenant for microsooft 2023-07-25 11:45:08 +05:30
catusax
0c334856bc Merge branch 'main' into main 2023-07-24 14:04:26 +08:00
catusax
ba0cf189de userid ass mfa session key 2023-07-24 12:00:30 +08:00
Lakhan Samani
9f52c08883 [app] bump authorizer-react 1.1.13 2023-07-24 11:56:56 +08:00
Lakhan Samani
80f3698f06 [app] bump authorizer-react 1.1.12 2023-07-24 11:56:56 +08:00
Lakhan Samani
2a2b7abc08 Add optional show_mobile_otp_screen 2023-07-24 11:56:56 +08:00
Lakhan Samani
27e3ed82e4 Update resend otp 2023-07-24 11:56:55 +08:00
Lakhan Samani
6077702626 fix: tests for otp refactor 2023-07-24 11:56:55 +08:00
Lakhan Samani
cf54fcef03 Fix tests 2023-07-24 11:56:55 +08:00
Lakhan Samani
2f849b8f0c Refactor code for otp 2023-07-24 11:56:55 +08:00
Lakhan Samani
85ca0f09bf [draft] Move sms verificaiton to otp models 2023-07-24 11:55:26 +08:00
Lakhan Samani
2fc438d810 [app] bump authorizer-react 1.1.13 2023-07-23 16:13:40 +05:30
Lakhan Samani
1796cace15 [app] bump authorizer-react 1.1.12 2023-07-23 11:30:43 +05:30
Lakhan Samani
43fdc826c4 Add optional show_mobile_otp_screen 2023-07-23 11:23:24 +05:30
catusax
e7652db89c add comments 2023-07-23 13:02:14 +08:00
Lakhan Samani
c80b0d7028 Merge pull request #368 from authorizerdev/fix-sms-verification-for-alldb
Move sms verificaiton to otp models
2023-07-23 10:04:18 +05:30
Lakhan Samani
55fc4b2608 Update resend otp 2023-07-23 10:03:37 +05:30
Lakhan Samani
fac333e195 fix: tests for otp refactor 2023-07-23 07:29:29 +05:30
catusax
5018462559 feat: add mfa session to secure otp login 2023-07-20 15:11:39 +08:00
Lakhan Samani
edb5412c17 Fix tests 2023-07-18 22:50:23 +05:30
Lakhan Samani
87a962504f Increase timeout for redis 2023-07-16 22:57:56 +05:30
Lakhan Samani
d04f79557a Refactor code for otp 2023-07-13 11:39:22 +05:30
Lakhan Samani
c20e9b810a Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix-sms-verification-for-alldb 2023-07-12 22:16:07 +05:30
Lakhan Samani
8d145bd5fe Merge pull request #369 from authorizerdev/feat-add-validate-cookie-api
feat: add resolver to validate browser session
2023-07-12 22:13:47 +05:30
Lakhan Samani
6fa0ad1809 feat: add resolver to validate browser session 2023-07-12 22:12:17 +05:30
Lakhan Samani
abe809ca68 [draft] Move sms verificaiton to otp models 2023-07-12 11:24:13 +05:30
Lakhan Samani
07f71e883b Add comments for twillio 2023-07-11 14:49:16 +05:30
Lakhan Samani
6cef9064c3 Update provider template for sms verification 2023-07-11 14:48:37 +05:30
Lakhan Samani
9ae616b6b5 Merge pull request #365 from JokerQyou/patch-1
Fix wrong response_type parsed when missing response_mode
2023-06-30 18:10:31 +05:30
Joker_
356428ea02 Fix wrong response_type parsed when missing response_mode 2023-06-29 23:10:44 +08:00
Lakhan Samani
7f47177741 Merge pull request #359 from MussieT/feat/sms_confirmation
Feat/sms confirmation
2023-06-13 09:38:23 +05:30
Mussie Teshome
9fb00544cd removed unwanted comment 2023-06-11 20:44:09 +03:00
Mussie Teshome
2b022d1058 Fix typo 2023-06-11 16:23:31 +03:00
Mussie Teshome
1c84d9f4a8 Merge branch 'authorizerdev:main' into feat/sms_confirmation 2023-06-11 16:05:29 +03:00
Mussie Teshome
0838b60fae Added VerifyMobileTest to the resolver 2023-06-11 16:03:16 +03:00
Mussie Teshome
325134466d Testing verify_mobile resolver 2023-06-11 16:02:46 +03:00
Mussie Teshome
58d9978dd5 Updated to test verification 2023-06-11 16:01:49 +03:00
Mussie Teshome
801d64e2f5 Twilio configuration 2023-06-11 16:00:30 +03:00
Mussie Teshome
dd3cc9de3a Verify mobile resolver 2023-06-11 16:00:07 +03:00
Mussie Teshome
8dc7366182 Updated mobile signup to send sms when service enabled 2023-06-11 15:59:53 +03:00
Mussie Teshome
7749534087 generated 2023-06-11 15:59:18 +03:00
Mussie Teshome
510f16e7b0 New resolver - Verify Moblie 2023-06-11 15:59:03 +03:00
Mussie Teshome
d5e83ea14f Schema update for SMSVerificationRequest 2023-06-11 15:58:50 +03:00
Mussie Teshome
b4a90de1d4 Updated to support disable sms verification request 2023-06-11 15:58:04 +03:00
Mussie Teshome
c525ad92f2 SQL Related dbs CRUD implementation for SMS 2023-06-11 15:57:14 +03:00
Mussie Teshome
9028682e93 Added SMSVerificationRequests model to automigrate 2023-06-11 15:56:40 +03:00
Mussie Teshome
3d6bfe4480 mongo implementation for the sms crud 2023-06-11 15:56:02 +03:00
Mussie Teshome
043af08bf0 Mongo collection for SMSVerificationRequest model 2023-06-11 15:55:11 +03:00
Mussie Teshome
0af78479fc Different dbs fn skeleton which fn yet not written 2023-06-11 15:54:23 +03:00
Mussie Teshome
096f686495 Added delete sms request to the interface 2023-06-11 15:52:33 +03:00
Mussie Teshome
c574c6a679 configure twilio via environment variables 2023-06-11 15:52:07 +03:00
Mussie Teshome
6428b74e64 twilio - new package 2023-06-11 15:50:09 +03:00
Mussie Teshome
aa3892025d New resolvers for sms requests 2023-06-11 15:49:25 +03:00
Mussie Teshome
b2f3d6eb80 sms verification requests model 2023-06-08 11:53:06 +03:00
Mussie Teshome
348cbf8c38 Add sms verification to collection 2023-06-08 11:52:39 +03:00
Mussie Teshome
8ac33a085c commented out sms twilio sender 2023-06-01 15:29:22 +03:00
Lakhan Samani
6c9b359081 Merge pull request #355 from minilikmila/fix/facebook-login
Modify the Facebook login authentication callback to enable user email access through the response body.
2023-05-29 10:32:27 +05:30
Mila Shumete
0fde46d274 setting on facebook user email method --- change the parameter(key) passed to get the email from map 2023-05-28 17:10:29 +03:00
Lakhan Samani
1a5b446894 Merge pull request #353 from authorizerdev/add-get-user-by-email
[server] add ability to get user by email
2023-05-20 09:50:59 +05:30
Lakhan Samani
930c934fdb [server] add ability to get user by email 2023-05-20 09:49:18 +05:30
Lakhan Samani
4e7074d75b Merge pull request #351 from miqe/feat/add_sender_name
Feat/add sender name
2023-05-16 14:07:18 +05:30
Michael Sahlu
bdfa045a43 add SENDER_NAME env 2023-05-16 00:59:45 +03:00
Michael Sahlu
a258399bde add Sender Name input 2023-05-16 00:59:01 +03:00
Michael Sahlu
55a25436a8 add SENDER_NAME in env variable query 2023-05-16 00:58:26 +03:00
Michael Sahlu
9fa402f5c8 add SENDER_NAME environment and types 2023-05-16 00:57:33 +03:00
Michael Sahlu
1111729ad4 add sender name / from name 2023-05-16 00:51:28 +03:00
Michael Sahlu
e56c2f58e5 add sender name on schema and resolver 2023-05-16 00:46:22 +03:00
Michael Sahlu
8dbd2556eb retrive sender name from env 2023-05-16 00:40:14 +03:00
Michael Sahlu
17bb077f3e add EnvKeySenderName for SENDER_NAME env variable 2023-05-16 00:39:25 +03:00
Lakhan Samani
f291417378 Merge pull request #350 from authorizerdev/fix/redirect-uri-error
[server]fix: error redirection for email verification
2023-05-12 16:43:38 +05:30
Lakhan Samani
f831379d27 revert change for forgot password 2023-05-12 16:39:02 +05:30
Lakhan Samani
a50f6becbd [server]fix: error redirection for email verification 2023-05-02 18:39:10 +05:30
Lakhan Samani
a6f6e0b18a Merge pull request #349 from darshvaghela/main 2023-04-24 09:32:38 +05:30
darshvaghela
3868157e11 refactored code 2023-04-23 17:31:24 +05:30
darshvaghela
d693c05483 Features enhancement (Disable/Enable) 2023-04-22 15:21:47 +05:30
Lakhan Samani
6f1fbf886d Merge pull request #346 from MussieT/feat/return-user-info-on-invitation
Feat/return user info on invitation
2023-04-20 13:59:49 +05:30
Mussie Teshome
b86487fda4 assert message and response is not null 2023-04-20 10:43:06 +03:00
Mussie Teshome
28d4ddeb50 Return the new emails only 2023-04-19 15:38:30 +03:00
Mussie Teshome
b9ab1d3761 return err on err 2023-04-19 15:31:57 +03:00
Mussie Teshome
a5b643e127 removed unnecessary comment 2023-04-19 15:19:17 +03:00
Mussie Teshome
691664e629 Invite members resolver updated to return user info 2023-04-19 14:46:27 +03:00
Mussie Teshome
efb67a9538 New response type for invite members 2023-04-19 14:45:22 +03:00
Mussie Teshome
a0f2eeba3e golang package updates 2023-04-19 14:44:50 +03:00
Mussie Teshome
d1a0ccd790 package updates while running make clean && make 2023-04-19 14:44:17 +03:00
Lakhan Samani
729c23f578 [chore]: update authorizer-react 2023-04-15 14:14:47 +05:30
Lakhan Samani
a074f85391 [chore]: update contributing guide 2023-04-15 08:33:33 +05:30
Lakhan Samani
4e7ec6cb7b [chore]: add command to generate db template 2023-04-15 08:13:10 +05:30
Lakhan Samani
6d541cbfb9 fix: use normal mutex for cache 2023-04-10 15:33:59 +05:30
Lakhan Samani
1ebba7f2b7 Merge pull request #343 from authorizerdev/fix/session-storage
fix: session storage
2023-04-08 18:07:52 +05:30
Lakhan Samani
428a0be3db feat: add cache clear 2023-04-08 18:02:53 +05:30
Lakhan Samani
02c0ebb9c4 fix: session storage 2023-04-08 13:06:15 +05:30
Lakhan Samani
9a284c03ca fix: redis session 2023-04-03 10:26:27 +05:30
Lakhan Samani
c8fe05eabc Merge pull request #342 from authorizerdev/feat/default-oauth-configs
feat: add support for default response mode & type env
2023-04-01 17:42:02 +05:30
Lakhan Samani
48344ffd4c feat: add support for default response mode & type env
Resolves #341
2023-04-01 17:36:07 +05:30
Lakhan Samani
77f34e1149 Merge pull request #339 from authorizerdev/fix/webhooks
fix: allow multiple hooks for same event
2023-03-29 07:31:46 +05:30
Lakhan Samani
16136931a9 fix: add event description to webhook res 2023-03-29 07:31:07 +05:30
Lakhan Samani
c908ac94da fix: continue in case of error for register events 2023-03-29 07:29:44 +05:30
Lakhan Samani
6604b6bbdd fix: update dashboard ui for webhooks 2023-03-29 07:27:56 +05:30
Lakhan Samani
2c227b5518 chore: delete couchbase container after tests 2023-03-29 07:10:36 +05:30
Lakhan Samani
e822b6f31a fix: queries for webhooks + improve tests 2023-03-29 07:06:33 +05:30
Lakhan Samani
a38e9d4e6c fix: rename title -> event_description 2023-03-26 07:48:06 +05:30
Lakhan Samani
deaf1e2ff7 fix: allow multiple hooks for same event 2023-03-26 07:20:45 +05:30
Lakhan Samani
f324976801 Merge pull request #338 from authorizerdev/fix/accessibility
fix: accessibility
2023-03-25 10:48:49 +05:30
Lakhan Samani
fad90ce1a8 fix: accessibility
Resolves #337
2023-03-25 10:48:09 +05:30
Lakhan Samani
df406ba053 Merge pull request #332 from authorizerdev/fix/open-id
fix: add missing info for openid config
2023-03-07 08:44:26 +05:30
Lakhan Samani
4a7877a21b fix: remove duplicate files 2023-03-04 16:13:31 +05:30
Lakhan Samani
79089cc009 Merge pull request #330 from productdevbook/patch-1
feat: github sponsor
2023-03-04 16:12:52 +05:30
Lakhan Samani
149d0cac7a fix: add missing info for openid config
Resolves #304
2023-03-04 16:11:37 +05:30
Lakhan Samani
8863140e75 Create FUNDING.yaml 2023-03-03 08:11:38 +05:30
Mehmet
b8ffadd36c Create FUNDING.yml 2023-03-02 13:07:05 +03:00
Lakhan Samani
7dd20128af Merge pull request #329 from authorizerdev/fix/add-sub-user-info
[server][fix]: add sub to userinfo
2023-02-28 12:52:21 +05:30
Lakhan Samani
19f5ff61c0 [server][fix]: add sub to userinfo
Resolves: #327
2023-02-28 12:51:11 +05:30
Lakhan Samani
146707d062 Merge pull request #328 from authorizerdev/feat/add-microsoft-login
feat: add microsoft login
2023-02-26 06:05:42 +05:30
Lakhan Samani
0810c4a201 chore: update app authorizer-react 1.1.8 2023-02-26 06:05:15 +05:30
Lakhan Samani
3603af9f84 feat: add microsoft login 2023-02-26 05:23:02 +05:30
Lakhan Samani
1ac8ba4ce0 Merge pull request #324 from authorizerdev/fix/neon-db-support
[server]: fix support for neondb
2023-02-10 18:08:22 +05:30
Lakhan Samani
cdcdc444b2 [server]: fix support for neondb
Update gorm/postgres driver version 1.4.7
2023-02-10 10:39:53 +05:30
Lakhan Samani
330f35f2fc Merge pull request #322 from authorizerdev/fix/use-scopes-as-string
[server] use scope string instead of string array in tokens
2023-02-08 09:41:17 +05:30
Lakhan Samani
70242debe1 [server] fix scope response type + add extra claims to access token 2023-02-08 09:39:08 +05:30
Lakhan Samani
4018da6697 [server] use scope string instead of string array in tokens 2023-02-07 01:13:03 +05:30
Lakhan Samani
a73c6ee49e Merge pull request #319 from authorizerdev/fix/arangodb-connection 2023-02-06 21:32:32 +05:30
Lakhan Samani
c23fb1bb32 [server] update arangodb version for test 3.10.3 2023-02-06 20:39:22 +05:30
Lakhan Samani
270853a6a3 [server] add support for arangodb cert, username, password
Adding support for db username, password and ca cert will
enable users to connect arangodb cloud platform
2023-02-06 18:14:19 +05:30
Lakhan Samani
2d0346ff23 [server] remove unused error condition for couchbase 2023-02-05 11:03:26 +05:30
Lakhan Samani
4b26e1ce85 [server] fix bucket creation for couchbase
Run create bucket query only if bucket is not found.
Required while running couchbase cloud version
2023-02-05 11:01:20 +05:30
Lakhan Samani
8212e81023 [server] common util for couchbase syntax 2023-02-02 13:06:18 +05:30
Lakhan Samani
642581eefd [server] Add COUCHBASE_BUCKET_RAM_QUOTA
Resolves #317
2023-02-02 12:43:17 +05:30
Lakhan Samani
b7357dde21 [server] fix primary index creation for couchbase 2023-02-02 12:28:52 +05:30
Lakhan Samani
a1df2ce31f [server] use encryption_key for couchbase env as hash is reserved keyword 2023-01-31 11:18:20 +05:30
Lakhan Samani
748761926d [server] fix make command 2023-01-25 05:19:01 +05:30
Lakhan Samani
d632195ba5 Merge pull request #315 from authorizerdev/feat/couchbase
feat: add couchbase provider
2023-01-19 23:32:20 +05:30
Lakhan Samani
25970f80e1 [app] update authorizer-react 2023-01-19 23:30:54 +05:30
Lakhan Samani
a52b7c86e7 [server] add couchbase to all db test 2023-01-19 23:29:48 +05:30
Lakhan Samani
504d0f34d7 [server] add couchbase envs 2023-01-19 23:28:21 +05:30
Lakhan Samani
44879f1a8f feat: add couchbase provider 2023-01-18 01:38:00 +05:30
Lakhan Samani
b39f0b87fd Merge pull request #313 from authorizerdev/feat/add-get-user
Add get user api for admin
2023-01-05 20:17:48 +05:30
Lakhan Samani
b2423140e2 Add get user api for admin 2023-01-05 20:16:41 +05:30
Lakhan Samani
4f810d2f8b Merge pull request #307 from authorizerdev/feat/mobile-basic-auth
feat: add mobile based basic auth
2022-12-25 03:28:55 +05:30
Lakhan Samani
313b510ba1 feat: add signup + login using mobile 2022-12-25 03:22:42 +05:30
Lakhan Samani
105d9be685 Merge pull request #310 from Lentech-AS/main
Rnd warning fixes
2022-12-24 09:18:07 +05:30
Leander Gangsø
bc68b61879 fix: oauth2.NoContext is deprecated
using context.TODO instead
2022-12-23 21:12:11 +01:00
Leander Gangsø
2847300bf6 fix: update deprecated func since go 1.16
might want to update the docs if you accept this, as it states go > 1.15 is required
2022-12-23 21:12:11 +01:00
Leander Gangsø
d438480f37 fix: remove unused formatting directive 2022-12-23 21:12:11 +01:00
Lakhan Samani
da29f9d055 Merge pull request #308 from Lentech-AS/main
change toast position to top-right
2022-12-23 07:10:11 +05:30
Leander Gangsø
f29256a8f5 change toast position to top-right 2022-12-22 19:53:18 +01:00
Lakhan Samani
1eb8965f98 feat: add mobile based basic auth 2022-12-21 23:14:24 +05:30
Lakhan Samani
1c4e29fa7c fix: access_token renew + web_message redirect 2022-11-29 05:27:29 +05:30
Lakhan Samani
7a28795fa0 Merge pull request #302 from authorizerdev/fix/sql-user-deletion
fix(sql): user deletion
2022-11-25 22:46:57 +05:30
Lakhan Samani
f5db00beb0 fix(sql): user deletion
Resolves: https://github.com/authorizerdev/docs/issues/18
2022-11-25 22:45:23 +05:30
Lakhan Samani
d515a1f41d chore: update app 1.1.4 2022-11-25 22:40:14 +05:30
Lakhan Samani
c948c98e94 Merge pull request #298 from authorizerdev/fix/url-parsing
fix: remove extra slash from host
2022-11-24 19:13:50 +05:30
Lakhan Samani
e985e096bc fix: remove extra slash from host 2022-11-24 12:58:04 +05:30
Lakhan Samani
70bab70ead fix: validating id_token 2022-11-23 22:03:08 +05:30
Lakhan Samani
6ddaf88e3f Merge pull request #294 from authorizerdev/fix/sql-unique-phone-constraint
fix(server): unique constraint for phone_number on mssql
2022-11-17 23:10:55 +05:30
Lakhan Samani
f8bcd0fe51 fix(server): unique constraint for phone_number on mssql 2022-11-17 23:08:17 +05:30
Lakhan Samani
16c4b8ab76 fix(server): global logging 2022-11-17 10:35:38 +05:30
Lakhan Samani
0788c5ff5e fix(server): default loglevel via cli arg 2022-11-17 10:27:55 +05:30
Lakhan Samani
2d5d38de02 fix(server): gorm logging
- add support for LOG_LEVEL env var (Resolves #271)
2022-11-17 10:25:00 +05:30
Lakhan Samani
0dd06d9afd fix(server): basic auth check 2022-11-17 05:44:40 +05:30
Lakhan Samani
d2f472a9cf Merge pull request #293 from luclu7/main
fix: codeVerifier shouldn't be empty for basic auth
2022-11-17 05:44:01 +05:30
Luclu7
4678193300 fix: codeVerifier shouldn't be empty for basic auth 2022-11-16 23:31:39 +01:00
Lakhan Samani
67be8ae285 feat(server): allow using client_id & secret from basic auth header in token endpoint 2022-11-16 22:40:45 +05:30
Lakhan Samani
f9d2130c65 Merge pull request #270 from authorizerdev/fix/oauth-provider
fix(server): authorizer openid flow
2022-11-16 12:27:30 +05:30
Lakhan Samani
bb2a42a1db fix: update app package 2022-11-16 12:20:32 +05:30
Lakhan Samani
f857c993c8 Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/oauth-provider 2022-11-16 11:47:47 +05:30
Lakhan Samani
824f286b9b Merge pull request #290 from authorizerdev/feat/plain-html-editor
feat: add plain html editor for email templates
2022-11-16 11:31:42 +05:30
anik-ghosh-au7
ecefe12355 chore: format graphql schema 2022-11-15 22:39:04 +05:30
anik-ghosh-au7
5c8f9406f6 fix: email template resolver and for matting changes 2022-11-15 22:12:14 +05:30
Lakhan Samani
75a547cfe2 fix: other auth recipes for oidc idp + remove logs 2022-11-15 21:45:08 +05:30
Lakhan Samani
579899c397 fix(server): creepy @@ string split logic for auth_token 2022-11-13 01:22:21 +05:30
Lakhan Samani
9320f1cb07 fix(server): add flow comment 2022-11-13 00:40:28 +05:30
Lakhan Samani
c09558043e fix(server): spacing 2022-11-13 00:16:22 +05:30
Lakhan Samani
49556b1709 fix: openid flow 2022-11-12 23:54:37 +05:30
Lakhan Samani
4775641431 Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/oauth-provider 2022-11-10 22:51:13 +05:30
anik-ghosh-au7
ae84213e34 fix: allow design variable empty value for email templates 2022-11-09 22:54:05 +05:30
anik-ghosh-au7
87bf1c3045 fix: allow design variable empty value for email templates 2022-11-09 22:48:12 +05:30
Lakhan Samani
e525877467 fix: latest tag 2022-11-09 22:27:52 +05:30
anik-ghosh-au7
b467e7002d feat: add plain html email template editor 2022-11-09 22:17:41 +05:30
Lakhan Samani
78bdd10a15 Merge pull request #289 from authorizerdev/fix/build
Fix/build
2022-11-09 22:08:13 +05:30
Lakhan Samani
512fd4f1f7 chore: add multi arch setup 2022-11-09 17:36:53 +05:30
Lakhan Samani
67da4a49e4 chore: use zip for windows assets 2022-11-09 16:07:27 +05:30
Lakhan Samani
48deae1d11 chore: fix multi arch build 2022-11-09 15:02:37 +05:30
Lakhan Samani
1f2ded4219 chore: fix tags 2022-11-09 14:15:09 +05:30
Lakhan Samani
cb5af1e679 use non c binding for sqlite 2022-11-09 13:07:20 +05:30
Lakhan Samani
27160ecbd5 chore: fix arm dependencies 2022-11-09 09:46:27 +05:30
Lakhan Samani
c6c3af1114 chore: update dependencies for cross compile 2022-11-09 04:31:04 +05:30
Lakhan Samani
e54b7f18f0 chore: add dependencies for cross compile 2022-11-09 04:25:15 +05:30
Lakhan Samani
a18046748b chore: add cgo param to gox 2022-11-09 04:09:55 +05:30
Lakhan Samani
1bff6720fc chore: install gox 2022-11-09 03:59:20 +05:30
Lakhan Samani
024ffd85f3 chore: fix yaml indentation 2022-11-09 03:52:47 +05:30
Lakhan Samani
e171820614 chore: use checkout@v3 2022-11-09 03:51:08 +05:30
Lakhan Samani
19f9caf478 chore: test gox + buildx 2022-11-09 03:47:28 +05:30
Lakhan Samani
4afd544c41 feat(server): add allowed_roles in access_token + refresh_token 2022-11-07 07:11:23 +05:30
Lakhan Samani
307c6f7d15 fix: refresh token login method claim 2022-11-04 01:40:18 +05:30
Lakhan Samani
bbc6394cf3 Merge pull request #286 from Pjort/main
fixed validation of refresh token
2022-11-03 20:10:40 +05:30
Pjort Kat
63c8e2e55f fixed validation of refresh token 2022-11-03 11:51:59 +01:00
Lakhan Samani
b224892a39 fix: minor space 2022-11-02 08:48:56 +05:30
Lakhan Samani
13edf1965c Merge pull request #285 from authorizerdev/development
feat(server): add jwt claims as part of validation endpoint
2022-11-01 10:15:08 +05:30
Lakhan Samani
1f220a5205 add jwt claims as part of validation endpoint 2022-11-01 10:12:01 +05:30
Lakhan Samani
32fb954a1c Merge pull request #281 from authorizerdev/development
chore: 1.1.22-rc.0
2022-10-25 08:27:02 +05:30
Lakhan Samani
65eadb66fa chore: format 2022-10-25 08:25:23 +05:30
Lakhan Samani
9ce53eb8e8 Merge branch 'development' of https://github.com/authorizerdev/authorizer into development 2022-10-25 08:21:32 +05:30
Lakhan Samani
3b196f074b Merge pull request #280 from authorizerdev/feat/user-roles-multi-select
feat: add user roles multi select input
2022-10-25 08:19:08 +05:30
Lakhan Samani
f2fe584793 feat: add support for SMTP LocalName
Resolves #274
2022-10-25 08:18:29 +05:30
Lakhan Samani
287b952dad fix: forgot password redirect from app 2022-10-24 11:37:42 +05:30
Lakhan Samani
e690066652 fix(server):give higher preference to redirect_uri
While using forgot_password redirect URI was ignored if not present

Resolves #275
2022-10-24 11:15:36 +05:30
anik-ghosh-au7
0f67d74657 feat: add user roles multi select input 2022-10-23 22:59:17 +05:30
Lakhan Samani
274909b7c9 feat: add nonce variable to create auth token 2022-10-23 21:08:08 +05:30
Lakhan Samani
549385e5df Merge branch 'development' of https://github.com/authorizerdev/authorizer into fix/oauth-provider 2022-10-23 16:56:25 +05:30
Lakhan Samani
6e09307c22 Merge pull request #279 from authorizerdev/fix/sql-server-unique-index
fix(server): sql server not allow multiple null
2022-10-21 22:00:55 +05:30
Lakhan Samani
7fc69dcc55 fix(server): sql server not allow multiple null
multiple null values for unique constrained column
is not allowed on sqlserver

Resolves #278
2022-10-21 22:00:16 +05:30
Lakhan Samani
8449821d1b fix(server): dynamodb tests + provider config 2022-10-21 15:55:54 +05:30
Lakhan Samani
476bdf00fc fix(server): open_id config 2022-10-21 11:21:21 +05:30
Lakhan Samani
e41f123866 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-10-21 11:19:37 +05:30
Lakhan Samani
094782aeca fix(server): linting issues 2022-10-21 11:19:32 +05:30
Lakhan Samani
9630cbbc3e Merge pull request #277 from authorizerdev/development
chore(1.1.21.rc)
2022-10-21 11:17:14 +05:30
Lakhan Samani
1ac060136a fix: make env vars name more persistent 2022-10-20 16:27:00 +05:30
Lakhan Samani
c6019e650b fix: add manual code generation 2022-10-20 15:35:26 +05:30
Lakhan Samani
b2e0a3371f fix: revert nonce 2022-10-20 00:14:06 +05:30
Lakhan Samani
a68876a6f4 fix: comment 2022-10-19 23:55:47 +05:30
Lakhan Samani
2c867b0314 fix: issuer token endpoint 2022-10-19 23:41:08 +05:30
Lakhan Samani
74b858ac24 fix: binding 2022-10-19 23:39:48 +05:30
Lakhan Samani
fedc3173fe fix: get nonce from query request if possible 2022-10-19 23:36:33 +05:30
Lakhan Samani
de4381261e fix: add nonce to supported claims 2022-10-19 23:17:13 +05:30
Lakhan Samani
a916b8c32c fix: add nonce 2022-10-19 19:04:15 +05:30
Lakhan Samani
89f08b6d31 fix: redirect from app 2022-10-19 12:20:22 +05:30
Lakhan Samani
cc23784df8 fix: add code to login query params 2022-10-19 12:01:34 +05:30
Lakhan Samani
7ff3b3018a fix: add code to query params 2022-10-19 11:29:49 +05:30
Lakhan Samani
2b52932e98 fix: add code to other response methods 2022-10-19 09:03:00 +05:30
Lakhan Samani
c716638725 fix(server): revert the state & code_challenge validation 2022-10-18 23:24:19 +05:30
Lakhan Samani
252cd1fa2d fix: make code_challenge optional 2022-10-18 23:14:24 +05:30
Lakhan Samani
7c2693b086 fix: form post template 2022-10-18 23:03:55 +05:30
Lakhan Samani
eaa10ec5bc fix: error detection 2022-10-18 22:34:57 +05:30
Lakhan Samani
253128ca0c fix: query params for code response 2022-10-18 22:00:54 +05:30
Lakhan Samani
cddfe1e088 fix: response 2022-10-18 21:46:37 +05:30
Lakhan Samani
8e655bcb5b fix: authorize response 2022-10-18 21:29:09 +05:30
Lakhan Samani
9a411e673c fix: reponse 2022-10-18 21:08:53 +05:30
Lakhan Samani
346c8e5a47 fix: handle response 2022-10-16 22:16:37 +05:30
Lakhan Samani
3cd99fe5f6 fix: open id config 2022-10-16 21:03:37 +05:30
Lakhan Samani
2bd92d6028 feat: add form_post method 2022-10-16 20:46:54 +05:30
Lakhan Samani
844e867d96 Merge pull request #262 from manojown/feat/dynamo-db-support
Feat/dynamo db support
2022-10-14 09:42:08 +05:30
Lakhan Samani
ff805e3ef2 fix: add comments 2022-10-12 13:10:24 +05:30
Lakhan Samani
0115128ee7 fix(server): authorizer as oauth provider 2022-10-09 19:48:13 +05:30
manoj
820d294130 comments resolved for requireEnv, Provider and test env 2022-10-09 00:49:31 +05:30
manoj
e37472d498 update the local test url for dynamodb 2022-10-08 16:20:16 +05:30
manoj
589af96888 resolve conflict over the db models 2022-10-08 16:07:07 +05:30
manoj
dccc70e5c0 resolve conflict with main branch 2022-10-08 15:47:44 +05:30
Lakhan Samani
d8eceadd7f Merge pull request #267 from authorizerdev/fix/sqlserver-text-type
fix(server): text type for sql server 2019
2022-10-08 15:38:32 +05:30
Lakhan Samani
e6c4fdff26 fix(server): text type for sql server 2019
Resolves #266
2022-10-07 10:13:20 +05:30
manoj
896d8e046d remove the white space 2022-10-05 16:11:31 +05:30
manoj
a6d5d4af24 require env condition needed to be added for dynamodb 2022-10-05 15:52:17 +05:30
Manoj
cc4eaa2847 dynamod db index changes added to the schema 2022-10-05 15:32:32 +05:30
Manoj
dba8944565 provider: dynamo-db support added 2022-10-03 01:08:12 +05:30
Lakhan Samani
e760a5598e Merge pull request #260 from authorizerdev/chore/add-prettier-app
chore(app): add prettier
2022-10-02 22:40:16 +05:30
Lakhan Samani
f62a22619b chore(app): add prettier 2022-10-02 22:39:47 +05:30
Lakhan Samani
c32a7fa1e4 Merge pull request #259 from authorizerdev/chore/add-prettier-dashboard
chore(dashboard): add prettier
2022-10-02 22:38:06 +05:30
Lakhan Samani
399b97079d chore(dashboard): add prettier 2022-10-02 22:37:20 +05:30
Lakhan Samani
fe687cb0ca Merge pull request #258 from authorizerdev/fix/cookie-security-features
feat(dashboard): allow setting admin / app cookie security
2022-10-02 22:03:43 +05:30
Lakhan Samani
9cb011e921 feat(dashboard): allow setting admin / app cookie security
Fixes #233
2022-10-02 22:01:22 +05:30
Lakhan Samani
4e1bba2ba8 Merge pull request #246 from jerebtw/main
feat: add app & admin cookie secure variable to dashboard
2022-10-02 21:36:36 +05:30
ruessej
f1509f90f0 feat: Update generated and models_gen 2022-10-01 18:12:49 +02:00
Jerebtw
bd4d48c7c5 fix: schema.graphqls 2022-10-01 17:57:23 +02:00
Jerebtw
0e3242372b feat: add app & admin cookie secure variable to dashboard
Todo: Generate graphql (i don't work on my PC (Windows))
2022-10-01 17:48:05 +02:00
Lakhan Samani
89cea39c41 Merge pull request #257 from authorizerdev/chore/update-go-gin-1.18.1
chore: update go-gin server to 1.18.1
2022-10-01 17:42:00 +05:30
Lakhan Samani
570a0b9531 chore: update go-gin server to 1.18.1 2022-10-01 17:41:16 +05:30
Lakhan Samani
686b3a4666 Merge pull request #255 from authorizerdev/chore/update-go-1.19.1
chore: update golang to 1.19.1
2022-10-01 17:20:48 +05:30
Lakhan Samani
b266a14108 chore: update golang to 1.19.1 2022-10-01 15:17:11 +05:30
Lakhan Samani
e5972a0dee Merge pull request #254 from authorizerdev/feat/update-gqlgen-0.17.20
chore: update gqlgen to 0.17.20
2022-10-01 15:15:13 +05:30
Lakhan Samani
6f46f1e6ef chore: update gqlgen to 0.17.20 2022-09-30 15:37:59 +05:30
Lakhan Samani
cfbce17ab8 fix: set same site cookie to none for cross site 2022-09-28 18:42:42 +05:30
Lakhan Samani
aa6601e62c fix: same site cookie 2022-09-28 18:30:30 +05:30
Lakhan Samani
d8ea0c656f Merge pull request #247 from authorizerdev/fix/same-site-cookie
fix(server): use sameSite as lax by default for app cookie
2022-09-28 11:18:03 +05:30
Lakhan Samani
f5323e0eec fix(server): update comments for host & cookies 2022-09-28 10:36:56 +05:30
Lakhan Samani
b1bc7b5370 fix(server): set default app cookie to lax mode 2022-09-28 09:51:04 +05:30
Lakhan Samani
536fd87c3c fix: debug log 2022-09-27 06:45:38 +05:30
Lakhan Samani
f8c96a9fee Merge pull request #244 from authorizerdev/fix/remove-user-verification-request-when-deleted
fix: remove entries from otp + verification when user is deleted
2022-09-27 06:44:22 +05:30
Lakhan Samani
837fc781de fix: remove entries from otp + verification when user is deleted
Resolves #234
2022-09-27 00:27:36 +05:30
Lakhan Samani
640bb8c9ed chore: bump app/authorizer-react 1.1.2 2022-09-27 00:07:52 +05:30
Lakhan Samani
d9bba0bbe7 Merge pull request #243 from authorizerdev/fix/bool-env-vars-secure-cookie
fix: app & admin cookie secure variable type while persisting info
2022-09-27 00:03:31 +05:30
Lakhan Samani
f91ec1880f fix: app & admin cookie secure variable type while persisting info
Resolves #241
2022-09-27 00:01:38 +05:30
Lakhan Samani
19e2153379 Update README.md 2022-09-15 12:24:47 +05:30
Lakhan Samani
221009bf0a Merge pull request #229 from ruessej/main
feat: Add a option to disable httpOnly cookies
2022-09-15 11:22:27 +05:30
ruessej
6085c2d535 Fix incorrect type 2022-09-14 12:24:19 +02:00
Jerebtw
8e0c5e4380 Make the default value true 2022-09-14 11:56:48 +02:00
Lakhan Samani
21b70e4b26 Merge pull request #230 from authorizerdev/fix/github-oauth-scopes
fix: scope for github auth
2022-09-14 11:46:46 +05:30
Lakhan Samani
993693884d fix: scope for github auth 2022-09-14 11:45:38 +05:30
Lakhan Samani
ed849fa6f6 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-09-14 10:44:09 +05:30
Lakhan Samani
aec1f5df53 fix: github endpoint to get user emails 2022-09-14 10:44:01 +05:30
ruessej
195bd1bc6a Add a option to disable httpOnly cookies 2022-09-12 14:37:42 +02:00
Lakhan Samani
45b4c41bca Merge pull request #228 from Deep-Codes/main 2022-09-10 11:40:11 +05:30
Deepankar
63d486821e fix: lint 2022-09-10 11:39:01 +05:30
Deep-Codes
4b56afdc98 fix(type): __authorizer__ on window 2022-09-10 11:23:20 +05:30
Lakhan Samani
6455ff956a fix: remove varible log 2022-09-10 10:52:56 +05:30
Lakhan Samani
3898e43fff feat: add button to jwt config as json 2022-09-10 10:50:15 +05:30
Lakhan Samani
2c305e5bde Update README.md 2022-09-09 10:24:30 +05:30
Lakhan Samani
b8fd08e576 Update README.md 2022-09-09 09:29:27 +05:30
Lakhan Samani
6dafa45051 fix: invalid login message
Resolves #224
2022-09-03 21:48:33 +05:30
Lakhan Samani
ead3514113 chore: update railway template 2022-08-31 13:09:00 +05:30
Lakhan Samani
75a413e5f2 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-08-31 11:02:50 +05:30
Lakhan Samani
91bf0e2478 fix: use replace all 2022-08-31 11:02:46 +05:30
Lakhan Samani
7a1305cf96 Merge pull request #222 from Deep-Codes/main 2022-08-31 07:04:20 +05:30
Deep-Codes
ff5a6ec301 feat(server): add log to show PORT 2022-08-30 23:35:43 +05:30
Lakhan Samani
b7b97b4f8d Merge pull request #221 from Deep-Codes/main
fix(dashboard): users table overflow
2022-08-30 22:38:49 +05:30
Deep-Codes
d9bc989c74 fix(dashboard): users table overflow 2022-08-30 21:56:28 +05:30
Lakhan Samani
d1f80d4088 feat: add support for twitter login 2022-08-29 08:37:53 +05:30
Lakhan Samani
4b299f0da2 fix: log 2022-08-29 08:19:11 +05:30
Lakhan Samani
ed8006db4c Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-08-29 08:18:42 +05:30
Lakhan Samani
97f6c7d50a fix: authorize endpoint setting user session 2022-08-29 08:18:20 +05:30
Lakhan Samani
5e3f68a180 Merge pull request #216 from szczepad/feat/twitter-login
Feat/twitter login
2022-08-24 08:53:52 +05:30
szczepad
f73d1fc588 feat: Adds login via twitter 2022-08-22 09:25:10 +02:00
szczepad
aa232de426 fix: Uses whitespace as seperator for oauth scopes in state-string
This is necessary, as the previous delimiter (,) was being redacted
after a redirect. This resulted in the scopes not being correctly
parseable and the state not being fetched correctly after the
oauth-callback
2022-08-22 09:25:10 +02:00
Lakhan Samani
34ce754ef6 feat: bootstrap twitter login config 2022-08-22 09:03:29 +02:00
Lakhan Samani
5f385b2016 fix: remove unused file 2022-08-18 07:21:50 +05:30
Lakhan Samani
da7c17271e Merge pull request #215 from wabscale/main
fix: rootless container
2022-08-18 06:11:32 +05:30
John McCann Cunniff Jr
69fbd631ff fix: rootless container 2022-08-17 20:33:05 -04:00
Lakhan Samani
deb209e358 Update README.md 2022-08-15 22:28:43 +05:30
Lakhan Samani
ea6b4cbc8d Update README.md 2022-08-15 22:28:13 +05:30
Lakhan Samani
2f21a09b2e chore: bump app/authorizer-react 1.0.0 2022-08-15 21:06:57 +05:30
Lakhan Samani
4ab775f2c1 fix: apple & linkedin env config 2022-08-13 12:37:04 +05:30
Lakhan Samani
b6e8023104 Merge pull request #211 from authorizerdev/fix/email-template
fix email template
2022-08-13 11:58:07 +05:30
Lakhan Samani
4f1597e5d2 fix: update note on features 2022-08-13 11:57:03 +05:30
Lakhan Samani
4f81d1969e fix email template
- fix verification types
- add design to cassandra db provider for email_template
- fix default email verification types to include update_email
2022-08-13 11:34:24 +05:30
Lakhan Samani
ad3e615ac7 Merge pull request #210 from authorizerdev/fix/dashboard-ui
Fix/dashboard UI
2022-08-13 03:57:19 +05:30
anik-ghosh-au7
e9a2301d2b feat: [dashboard] add env options for multi factor auth 2022-08-11 17:50:45 +05:30
anik-ghosh-au7
48bbfa31af fix: template editor design 2022-08-11 17:08:23 +05:30
anik-ghosh-au7
d7f5f563cc fix: add design to email template 2022-08-11 16:45:59 +05:30
anik-ghosh-au7
6c29149fbe fix: email template editor 2022-08-11 15:08:50 +05:30
Lakhan Samani
bbd4d43317 fix: add padding to editor 2022-08-09 12:10:50 +05:30
Lakhan Samani
c4d2f62657 fix: clear form on close 2022-08-09 11:55:55 +05:30
Lakhan Samani
5d78bf178f fix: email template info 2022-08-09 11:41:51 +05:30
Lakhan Samani
58749497bd fix: payload example for webhook 2022-08-09 10:04:06 +05:30
Lakhan Samani
5c6e643efb Merge pull request #209 from authorizerdev/feat/send-email-based-on-template
feat: send email based on template
2022-08-09 09:17:29 +05:30
Lakhan Samani
7792cdbc5e fix: template respone & ui 2022-08-09 09:07:47 +05:30
Lakhan Samani
65803c3763 fix: remove todos 2022-08-09 01:53:21 +05:30
Lakhan Samani
81fce1a471 feat: send email based on template 2022-08-09 01:43:37 +05:30
Lakhan Samani
0714b4360b Merge pull request #206 from authorizerdev/feat/2fa
feat: add mutifactor authentication
2022-08-07 11:11:56 +05:30
Lakhan Samani
8f69d5746e Merge pull request #207 from authorizerdev/feat/email-template-ui
feat: email template UI + subject
2022-08-07 11:10:44 +05:30
Lakhan Samani
ebc11906ef Merge branch 'feat/2fa' of https://github.com/authorizerdev/authorizer into feat/2fa 2022-08-03 23:20:37 +05:30
Lakhan Samani
465a92de22 feat: add managing mfa 2022-08-03 23:20:23 +05:30
Lakhan Samani
a890013317 Update generate_otp.go 2022-08-02 18:26:05 +05:30
Lakhan Samani
587828b59b feat: add helper for updating all users 2022-08-02 14:12:36 +05:30
anik-ghosh-au7
85630a59c1 feat: add webhook payload example 2022-08-02 00:56:21 +05:30
anik-ghosh-au7
b4ef196bfb fix: update email template variables 2022-08-01 14:07:06 +05:30
anik-ghosh-au7
099b2a39b4 feat: add delete email template modal 2022-07-30 22:47:00 +05:30
anik-ghosh-au7
2d07baedf4 feat: fix update email template editor 2022-07-30 20:28:36 +05:30
anik-ghosh-au7
8b34e001ef feat: fix update email template editor 2022-07-30 20:15:49 +05:30
anik-ghosh-au7
617dcdde53 feat: fix update email template modal 2022-07-30 18:43:02 +05:30
anik-ghosh-au7
f2fb800323 feat: dashboard add email-template page 2022-07-30 16:05:35 +05:30
Lakhan Samani
236045ac54 feat: add resend otp test 2022-07-30 01:12:20 +05:30
Lakhan Samani
d89be44fe5 feat: add sending otp 2022-07-29 19:49:50 +05:30
Lakhan Samani
db4d711cba feat: add subject to email template 2022-07-29 16:15:57 +05:30
Lakhan Samani
0fc9e8ccaa feat: add EnvKeyIsEmailServiceEnabled 2022-07-29 16:00:12 +05:30
anik-ghosh-au7
4e3d73e767 feat: otp resolvers updated 2022-07-29 13:49:46 +05:30
anik-ghosh-au7
e3c58ffbb0 fix: login resolver multifactor auth 2022-07-28 11:18:06 +05:30
anik-ghosh-au7
f12491e42d fix: auth response schema updated 2022-07-27 15:28:12 +05:30
anik-ghosh-au7
d653fac340 Merge branch 'feat/2fa' of https://github.com/authorizerdev/authorizer into feat/2fa 2022-07-27 12:18:51 +05:30
anik-ghosh-au7
9fae8215d2 feat: dashboard - add actions to update is_multi_factor_auth_enabled 2022-07-27 12:18:32 +05:30
Lakhan Samani
4e23e49de4 fix: syntax 2022-07-25 18:08:07 +05:30
anik-ghosh-au7
ef22318d5c feat: add generate_otp util 2022-07-24 10:40:37 +05:30
anik-ghosh-au7
480438fb7a fix: remove duplicate code in verify otp resolver 2022-07-23 20:04:39 +05:30
Lakhan Samani
8db6649e5c Merge pull request #205 from anik-ghosh-au7/feat/2fa
update: verify otp resolver and test added
2022-07-23 18:37:04 +05:30
anik-ghosh-au7
49cc6033ab update: verify otp resolver and test added 2022-07-23 18:32:31 +05:30
Lakhan Samani
5d903ca170 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/2fa 2022-07-23 16:52:30 +05:30
Lakhan Samani
44280be25a feat: add resolver for verify_otp 2022-07-23 16:44:39 +05:30
Lakhan Samani
f6029fb7bf feat: use upsert for otp + implement otp methods for cassandradb 2022-07-23 16:39:35 +05:30
Lakhan Samani
22ae3bca54 feat: add otp implementation for arangodb 2022-07-23 16:06:52 +05:30
Lakhan Samani
1a27d91957 feat: add otp implementation for mongodb 2022-07-23 16:01:46 +05:30
Lakhan Samani
f6c67243b9 feat: add otp model + implementation for sql 2022-07-23 15:55:06 +05:30
Lakhan Samani
9ba1239c11 Merge pull request #204 from anik-ghosh-au7/main
fix: collections names
2022-07-23 15:46:35 +05:30
anik-ghosh-au7
ed7ed73980 fix: collections names 2022-07-23 15:44:56 +05:30
Lakhan Samani
9ef5f33f7a feat: add is_multi_factor_auth_enabled 2022-07-23 15:26:44 +05:30
Lakhan Samani
0f081ac3c8 Update README.md 2022-07-20 23:08:48 +05:30
Lakhan Samani
3aa0fb20ce Update CONTRIBUTING.md 2022-07-20 23:08:44 +05:30
Lakhan Samani
891c885f20 fix: webhook ui 2022-07-17 17:18:45 +05:30
Lakhan Samani
89606615dc Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-07-17 17:06:03 +05:30
Lakhan Samani
ecab47b2ea Merge pull request #202 from anik-ghosh-au7/feat/webhooks
Feat/webhooks
2022-07-17 17:05:51 +05:30
Lakhan Samani
882756ef3a fix: handle different response 2022-07-17 17:05:35 +05:30
anik-ghosh-au7
a208c87c29 update: webhooks 2022-07-17 16:50:58 +05:30
Lakhan Samani
70ea463f60 feat: handle empty response from webhook endpoint 2022-07-17 16:25:16 +05:30
anik-ghosh-au7
79c94fcaf0 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/webhooks 2022-07-17 16:03:21 +05:30
anik-ghosh-au7
3b925bb072 update: webhooks 2022-07-17 16:03:07 +05:30
anik-ghosh-au7
df17ea8f40 update: webhooks 2022-07-17 14:48:20 +05:30
anik-ghosh-au7
94066d4408 update: webhooks 2022-07-17 14:42:46 +05:30
Lakhan Samani
41468b5b60 Merge pull request #201 from authorizerdev/feat/add-email-template-apis
feat: add email template apis
2022-07-17 14:20:34 +05:30
anik-ghosh-au7
1c61fcc17a update: webhooks 2022-07-17 13:52:31 +05:30
Lakhan Samani
a102924fd7 fix: remove debug logs 2022-07-17 13:39:23 +05:30
anik-ghosh-au7
390846c85f update: webhooks 2022-07-17 13:38:18 +05:30
Lakhan Samani
a48b809a89 feat: add tests for email template resolvers 2022-07-17 13:37:34 +05:30
Lakhan Samani
cd46da60a0 feat: implement resolvers for email template 2022-07-17 12:32:01 +05:30
Lakhan Samani
50f52a99b4 fix: github user emails 2022-07-17 12:07:17 +05:30
anik-ghosh-au7
150b1e5712 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/webhooks 2022-07-17 11:48:54 +05:30
Lakhan Samani
1f7eee43e2 feat: add email template implementation for arangodb provider 2022-07-17 11:37:04 +05:30
Lakhan Samani
7c441fff14 feat: add email template implementation for cassandra provider 2022-07-17 11:21:51 +05:30
Lakhan Samani
647cc1d9bf feat: add email template implementation for mongodb provider 2022-07-17 11:01:47 +05:30
Lakhan Samani
97b1d8d66f Merge branch 'main' into feat/add-email-template-apis 2022-07-17 10:39:02 +05:30
Lakhan Samani
2cce1c4e93 fix: webhook update headers 2022-07-17 10:36:16 +05:30
anik-ghosh-au7
8b1511a07b update: webhooks 2022-07-16 23:10:05 +05:30
anik-ghosh-au7
a69dd95992 update: webhooks 2022-07-16 15:59:21 +05:30
anik-ghosh-au7
d3260f4f32 update: webhooks 2022-07-16 15:24:50 +05:30
anik-ghosh-au7
301bde4da2 update: webhooks 2022-07-16 09:53:29 +05:30
anik-ghosh-au7
913c5c94fb update: webhooks 2022-07-16 09:42:10 +05:30
Lakhan Samani
610896b6f5 fix: refs for email templatE 2022-07-15 22:13:00 +05:30
anik-ghosh-au7
33f79872be Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/webhooks 2022-07-15 22:12:29 +05:30
anik-ghosh-au7
8fc52d76dc fix: TT-69 2022-07-15 22:12:08 +05:30
Lakhan Samani
aa12757155 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/add-email-template-apis 2022-07-15 22:11:58 +05:30
Lakhan Samani
847c364ad1 fix: refs 2022-07-15 22:11:08 +05:30
anik-ghosh-au7
eabc943452 update: webhooks 2022-07-15 13:17:09 +05:30
anik-ghosh-au7
41a0f15e16 update: webhooks 2022-07-15 13:04:32 +05:30
Lakhan Samani
e2294c24d0 feat: add email template implementation for sql provider 2022-07-15 12:35:35 +05:30
anik-ghosh-au7
a3c0a0422c update: webhooks 2022-07-15 12:22:47 +05:30
anik-ghosh-au7
d837b1590a update: webhooks 2022-07-15 12:20:51 +05:30
Lakhan Samani
283e570ebb feat: init email template schema for all providers 2022-07-15 10:23:45 +05:30
Lakhan Samani
14c74f6566 feat: add email template schema 2022-07-15 10:12:24 +05:30
anik-ghosh-au7
8e655daa71 update: webhooks 2022-07-14 23:41:44 +05:30
Lakhan Samani
fed092bb65 fix: invite email template 2022-07-13 21:16:31 +05:30
Lakhan Samani
6d28290605 Merge pull request #199 from authorizerdev/fix/password-changing
fix(update_profile): changing password if not signed up via basic auth
2022-07-13 20:46:56 +05:30
Lakhan Samani
2de0ea57d0 fix(update_profile): changing password if not signed up via basic
Resolves #198
2022-07-13 20:45:21 +05:30
Lakhan Samani
f2886e6da8 fix: disable other db test for quick test 2022-07-12 11:57:46 +05:30
Lakhan Samani
6b57bce6d9 fix: cassandra + mongo + arangodb issues with webhook 2022-07-12 11:48:42 +05:30
Lakhan Samani
bfbeb6add2 fix: couple session deletion with user deletion 2022-07-12 08:42:32 +05:30
Lakhan Samani
1fe0d65874 feat: add support for planetscale
Resolves #195
2022-07-11 22:37:07 +05:30
Lakhan Samani
bfaa0f9d89 fix: make list webhooks params optional 2022-07-11 22:05:44 +05:30
Lakhan Samani
4f5a6c77f8 Merge pull request #194 from authorizerdev/feat/webhook
feat: add webhook apis + integrate in events
2022-07-11 19:56:48 +05:30
Lakhan Samani
018a13ab3c feat: add tests for webhook resolvers 2022-07-11 19:40:54 +05:30
Lakhan Samani
334041d0e4 fix: delete user event flow 2022-07-11 11:13:32 +05:30
Lakhan Samani
6a8309a231 feat: register event for revoke/enable access + delete user 2022-07-11 11:12:30 +05:30
Lakhan Samani
6347b60753 fix: rename revoke refresh token handler for better reading 2022-07-11 11:10:30 +05:30
Lakhan Samani
bbb064b939 feat: add register event 2022-07-11 10:42:42 +05:30
Lakhan Samani
e91a819067 feat: implement resolvers 2022-07-10 21:49:33 +05:30
Lakhan Samani
09c3eafe6b feat: add mongodb database methods for webhook 2022-07-09 12:23:48 +05:30
Lakhan Samani
bb51775d34 feat: add cassandradb database methods for webhook 2022-07-09 12:16:54 +05:30
Lakhan Samani
6d586b16e4 feat: add arangodb database methods for webhook 2022-07-09 11:44:14 +05:30
Lakhan Samani
e8eb62769e feat: add sql database methods for webhook 2022-07-09 11:21:32 +05:30
Lakhan Samani
0ffb3f67f1 fix: index for arangodb 2022-07-08 19:10:37 +05:30
Lakhan Samani
ec62686fbc feat: add database methods for webhookLog 2022-07-08 19:09:23 +05:30
Lakhan Samani
a8064e79a1 feat: add template for webhook db methods 2022-07-06 10:38:21 +05:30
Lakhan Samani
265331801f feat: add database models 2022-07-04 22:37:13 +05:30
Lakhan Samani
6a74a50493 feat: add support for scylladb
Resolves #177
2022-07-04 21:57:14 +05:30
Lakhan Samani
8c27f20534 Merge pull request #193 from authorizerdev/fix/invalidate-session-token
fix: add provider to token creation
2022-07-01 22:14:50 +05:30
Lakhan Samani
29c6003ea3 fix: remove test log 2022-07-01 22:04:58 +05:30
Lakhan Samani
ae34fc7c2b fix: update_env resolver 2022-07-01 22:02:34 +05:30
Lakhan Samani
2a5d5d43b0 fix: add namespace to session token keys 2022-06-29 22:24:00 +05:30
Lakhan Samani
e6a4670ba9 fix: add provider to token creation 2022-06-29 09:54:12 +05:30
Lakhan Samani
64d64b4099 feat: add ability to disable strong password 2022-06-18 15:31:57 +05:30
Lakhan Samani
88f9a10f21 Merge pull request #192 from authorizerdev/feat/apple-login
feat: add apple login
2022-06-16 09:48:02 +05:30
Lakhan Samani
4e08d4f8fd fix: update app 2022-06-16 09:47:16 +05:30
Lakhan Samani
1c4dda9299 fix: remove debug logs 2022-06-16 07:34:10 +05:30
Lakhan Samani
ab18fa5832 fix: use raw base64 url decoding 2022-06-15 21:55:41 +05:30
Lakhan Samani
484d0c0882 chore: update app 2022-06-14 16:39:06 +05:30
Lakhan Samani
be59c3615f fix: add comment for scope 2022-06-14 15:47:08 +05:30
Lakhan Samani
db351f7771 fix: remove debug logs 2022-06-14 15:45:06 +05:30
Lakhan Samani
91c29c4092 fix: redirect 2022-06-14 15:43:23 +05:30
Lakhan Samani
415b97535e fix: update scope param 2022-06-14 15:05:56 +05:30
Lakhan Samani
7d1272d815 fix: update scope for apple login 2022-06-14 14:41:31 +05:30
Lakhan Samani
c9ba0b13f8 fix: update scope for apple login 2022-06-14 13:37:05 +05:30
Lakhan Samani
fadd9f6168 fix: update scope for apple login 2022-06-14 13:11:39 +05:30
Lakhan Samani
395e2e2a85 fix: update scope for apple login 2022-06-14 12:35:23 +05:30
Lakhan Samani
6335084835 fix: add post method support for oauth callback 2022-06-14 12:17:43 +05:30
Lakhan Samani
eab336cd3d fix: apple login params 2022-06-14 12:06:46 +05:30
Lakhan Samani
f4691fca1f fix: id token parsing 2022-06-14 11:38:04 +05:30
Lakhan Samani
341d4fbae5 fix: scope for apple login 2022-06-14 11:21:26 +05:30
Lakhan Samani
e467b45ab1 fix: apple client secret field 2022-06-14 11:11:09 +05:30
Lakhan Samani
7edfad3486 fix: apple client secret field 2022-06-14 10:56:47 +05:30
Lakhan Samani
80578b88ac feat: update app 2022-06-13 07:37:26 +05:30
Lakhan Samani
5646e7a0e7 feat: add test code to process apple user 2022-06-12 18:30:33 +05:30
Lakhan Samani
53a592ef63 feat: add base for apple login 2022-06-12 14:49:48 +05:30
Lakhan Samani
3337dbd0a4 Merge pull request #191 from authorizerdev/fix/session-invalidation
fix: session invalidation
2022-06-12 09:08:57 +05:30
Lakhan Samani
82a2a42f84 fix: user session access 2022-06-12 00:27:21 +05:30
Lakhan Samani
ac49b5bb70 fix: session tests 2022-06-11 19:24:53 +05:30
Lakhan Samani
926ab07c07 fix: session invalidation 2022-06-11 19:10:39 +05:30
Lakhan Samani
7a2dbea019 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-06-09 23:43:28 +05:30
Lakhan Samani
dff50097e8 feat: add support for cockroachdb 2022-06-09 23:43:21 +05:30
Lakhan Samani
aff9d3af20 Merge pull request #187 from authorizerdev/fix-parallel-access
fix: parallel access of env vars
2022-06-09 23:13:34 +05:30
Lakhan Samani
02eb1d6677 fix: add const for test env 2022-06-09 23:13:22 +05:30
Lakhan Samani
78a673e4ad fix: fix parallel access of env vars 2022-06-08 09:50:30 +05:30
Lakhan Samani
e0d8644264 fix: role validation while signup 2022-06-07 08:00:30 +05:30
Lakhan Samani
d8c662eaad fix: dashboard roles 2022-06-07 07:30:01 +05:30
Lakhan Samani
6d1d259f71 Merge pull request #182 from authorizerdev/feat/add-linkedin-login
feat: add linkedin login
2022-06-06 22:09:08 +05:30
Lakhan Samani
2841853d37 feat: add linkedin login 2022-06-06 22:08:32 +05:30
Lakhan Samani
360dd3c3bd fix: redirect uri 2022-06-05 22:46:56 +05:30
Lakhan Samani
c6add0cca6 fix: give higher priority to authorizer url 2022-06-05 22:13:10 +05:30
Lakhan Samani
7ac6252aac fix: app login page signup url
add debug logs
2022-06-05 21:44:16 +05:30
Lakhan Samani
5d2d1c342b fix: allow setting host for cassandradb without prot 2022-06-05 12:13:55 +05:30
Lakhan Samani
6da0a85936 fix: remove unused code 2022-06-04 09:26:02 +05:30
Lakhan Samani
116972d725 feat: add support for ScyllaDB
Resolves #177
2022-06-04 08:59:26 +05:30
Lakhan Samani
d1e1e287db Merge pull request #174 from authorizerdev/fix/memory-store
fix: replica cache consistency
2022-06-01 23:47:55 +05:30
Lakhan Samani
a7f04f8754 fix: fix mutex for testing purpose 2022-05-31 15:06:53 +05:30
Lakhan Samani
69b56c9912 fix: disable mutex for testing purpose 2022-05-31 15:00:11 +05:30
Lakhan Samani
98015708a2 fix: bool flag for redis 2022-05-31 13:27:43 +05:30
Lakhan Samani
1b5a7b8fb0 fix: don't allow redis disabling from dashboard 2022-05-31 13:26:03 +05:30
Lakhan Samani
8b9bcdfdbe fix: message 2022-05-31 13:24:24 +05:30
Lakhan Samani
ba429da05f fix: env query 2022-05-31 13:18:42 +05:30
Lakhan Samani
7c7bb42003 fix: message 2022-05-31 13:14:08 +05:30
Lakhan Samani
eeff88c853 fix: env saving 2022-05-31 13:11:54 +05:30
Lakhan Samani
cf8762b7a0 fix: slice envs 2022-05-31 08:14:03 +05:30
Lakhan Samani
c61c3024ec fix: upgrade tests 2022-05-30 12:47:50 +05:30
Lakhan Samani
7e3bd6a721 fix: import cycle issues 2022-05-30 11:54:16 +05:30
Lakhan Samani
1146468a03 fix: memory store upgrade in token helpers 2022-05-30 11:00:00 +05:30
Lakhan Samani
268b22ffb2 fix: memory store upgrade in resolvers 2022-05-30 09:19:55 +05:30
Lakhan Samani
43359f1dba fix: update store method till handlers 2022-05-29 17:22:46 +05:30
Lakhan Samani
1941cf4299 fix: move sessionstore -> memstore 2022-05-27 23:20:38 +05:30
Lakhan Samani
7b13034081 fix: dashboard 2022-05-25 16:34:54 +05:30
Lakhan Samani
7c16900618 Merge pull request #168 from anik-ghosh-au7/fix/app-routes
update: separate routes for login and signup added
2022-05-25 16:07:15 +05:30
Lakhan Samani
d722fe258d Merge pull request #173 from authorizerdev/feat/logging
feat: add logging system
2022-05-25 15:07:28 +05:30
Lakhan Samani
99dc5ee572 fix: remove unused code 2022-05-25 15:05:51 +05:30
Lakhan Samani
8bee421d0a feat: add flag for log level 2022-05-25 15:04:26 +05:30
Lakhan Samani
714b79e4ab fix: format logs 2022-05-25 12:30:22 +05:30
Lakhan Samani
d886d780b4 fix: replace all logs 2022-05-24 12:50:33 +05:30
Lakhan Samani
d7bb10fd21 feat: add loggging to all resolvers 2022-05-24 12:42:29 +05:30
Lakhan Samani
f5515bec28 fix: merge conflict 2022-05-23 11:54:46 +05:30
Lakhan Samani
b35d86fd40 feat: add logs for http handlers 2022-05-23 11:52:51 +05:30
anik-ghosh-au7
a638f02014 update: seperate routes for login and signup added 2022-05-18 20:04:29 +05:30
Lakhan Samani
2c4bc9adb6 Merge pull request #161 from akash-dutta-au7/fix/env-page-new
Update Dashboard UI
2022-05-15 17:11:13 +05:30
Lakhan Samani
5884802e60 Merge pull request #166 from Vicg853/fix/role-update
Fix/role update
2022-05-15 17:04:42 +05:30
Vicg853
241f977b2a Fixing related files 2022-05-14 23:21:45 -03:00
anik-ghosh-au7
ed855a274a fix: app style remove unnecessary code 2022-05-14 20:20:21 +05:30
Vicg853
049ea64475 Merge branch 'main' of github.com:Vicg853/authorizer into fix/role-update 2022-05-14 05:07:13 -03:00
Vicg853
5e4f34c889 Fixing login isValidRole usage 2022-05-14 04:37:36 -03:00
Lakhan Samani
ab717d956a fix: update role test 2022-05-13 07:49:45 +05:30
Lakhan Samani
6209c4d506 Merge pull request #165 from Vicg853/fix/role-update
Unable to update user role fix
2022-05-13 07:38:45 +05:30
Lakhan Samani
2bc4c74930 fix: remove old logs 2022-05-13 07:28:31 +05:30
Vicg853
1efa419cdf Clean up 2022-05-12 16:43:07 -03:00
Vicg853
4ceb6db4ba Adding possible test error cause comment 2022-05-12 16:40:49 -03:00
Vicg853
9edc8d0fb5 Inverted userRoles by role fix. Roles can now be updated 2022-05-12 16:40:19 -03:00
Lakhan Samani
da0fcb109b feat: setup logours for logging 2022-05-13 00:47:01 +05:30
akash.dutta
3e51a7bd01 login page vertically responsive 2022-05-12 17:06:18 +05:30
akash.dutta
28bed69b2e login page vertically responsive 2022-05-12 15:10:24 +05:30
anik-ghosh-au7
0433d64737 fix: dom nesting bugs 2022-05-12 12:27:25 +05:30
Lakhan Samani
773213e5a4 fix: clean test data 2022-05-11 20:25:57 +05:30
akash.dutta
de44c40de5 login page fixed 2022-05-09 15:46:42 +05:30
akash.dutta
a7fa988bf0 tooltiip added to Invite member button 2022-05-08 12:48:26 +05:30
akash.dutta
538a2d0b59 tooltiip added to Invite member button 2022-05-08 12:43:52 +05:30
akash.dutta
f519f0eb0e tooltiip added to Invite member button 2022-05-08 09:35:50 +05:30
akash.dutta
d5ad4a6e55 blue border on navlink removed 2022-05-07 22:34:26 +05:30
akash.dutta
d9b49ca932 login page responsive 2022-05-07 22:18:04 +05:30
akash.dutta
7c5aab7bf3 all components updated, uncontrolled input error handled 2022-05-07 22:10:29 +05:30
akash.dutta
c783e101d5 test-pull req 2022-05-03 19:53:21 +05:30
akash.dutta
ebccfb18cd test-pull req 2022-05-03 19:50:01 +05:30
Lakhan Samani
b7aeff57af fixes #160 2022-04-30 12:45:08 +05:30
Lakhan Samani
075c287f34 feat: add support for database cert, key, ca-cert 2022-04-23 17:52:02 +05:30
Lakhan Samani
4778827545 Merge pull request #144 from authorizerdev/feat/casandra-db
feat: add support for cassandra db
2022-04-22 21:26:10 +05:30
Lakhan Samani
39c2c364d9 feat: add support for db username, password, host, port 2022-04-22 21:24:39 +05:30
Lakhan Samani
961f2271c1 fix: tests 2022-04-22 19:56:55 +05:30
Lakhan Samani
aaf0831793 feat: add users queries 2022-04-22 16:45:49 +05:30
Lakhan Samani
27cb41c54c feat: add verification_request queries 2022-04-22 11:52:15 +05:30
Lakhan Samani
718b2d535f feat: add session queries 2022-04-21 18:11:15 +05:30
Lakhan Samani
ed6a1ceccc feat: add env queries 2022-04-21 17:54:33 +05:30
Lakhan Samani
fd52d6e5d3 feat: add casandradb provider 2022-04-21 12:36:22 +05:30
Lakhan Samani
325aa88368 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/casandra-db 2022-04-20 23:32:02 +05:30
Lakhan Samani
75e44ff698 fix: cors error for x-authorizer-url 2022-04-10 14:43:19 +05:30
Lakhan Samani
d5f1c5a5eb Resolves #156 2022-04-02 17:34:50 +05:30
Lakhan Samani
39947f1753 Merge pull request #155 from authorizerdev/fix/gateway-based-setup
fix: setting the cookie for proxy setup
2022-03-30 11:51:20 +05:30
Lakhan Samani
4fa9f79c3f fix: setting the cookie for proxy setup 2022-03-30 11:50:22 +05:30
Lakhan Samani
fe73c2f6f8 Update README.md 2022-03-26 07:00:01 +05:30
Lakhan Samani
4a3e3633ea fix: default access token expiry time 2022-03-25 20:29:00 +05:30
Lakhan Samani
dbbe36f6b5 Merge pull request #154 from MedvedewEM/enhancement/access_token_expiry_time
enhancement: add access_token_expiry_time env variable
2022-03-25 18:57:53 +05:30
egor.medvedev
819dd57377 Merge branch 'authorizerdev/authorizer:main' into main 2022-03-25 16:13:46 +03:00
egor.medvedev
044b025ba2 enhancement: add access_token_expiry_time env variable 2022-03-25 15:21:20 +03:00
Lakhan Samani
41b5f00b83 fix: client id make readonly 2022-03-25 00:32:21 +05:30
Lakhan Samani
3c31b7fdc7 Merge pull request #153 from anik-ghosh-au7/develop
fix: update jwt keys persisting old values
2022-03-24 23:34:20 +05:30
Anik Ghosh
7e91c6ca28 fix: update jwt keys persisting old values 2022-03-24 22:53:54 +05:30
Lakhan Samani
b1b43a41ca fix: resetting the keys 2022-03-24 22:19:30 +05:30
Lakhan Samani
f969495178 fix: generate new keys modal 2022-03-24 22:09:32 +05:30
Lakhan Samani
3c4c128931 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-03-24 21:51:00 +05:30
Lakhan Samani
003cec4f48 feat: add tests for revoke and enable access 2022-03-24 21:50:39 +05:30
Lakhan Samani
3e488155dc Merge pull request #152 from anik-ghosh-au7/feat/generate-new-keys
Feat/generate new keys
2022-03-24 21:34:28 +05:30
Anik Ghosh
4f4a3a91e1 generate-keys-modal updated 2022-03-24 21:29:43 +05:30
Anik Ghosh
a3d9783aef mutation to update jwt vars added 2022-03-24 21:08:10 +05:30
Anik Ghosh
7d77396657 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/generate-new-keys 2022-03-24 19:30:32 +05:30
Lakhan Samani
7a18fc6312 fix: add test to resolvers test 2022-03-24 19:23:43 +05:30
Lakhan Samani
90e2709eeb feat: add mutation to generate new jwt secret & keys
Resolves: #150
2022-03-24 19:21:52 +05:30
Anik Ghosh
4c4743ac24 generate-keys-modal added in dashboard 2022-03-24 18:23:22 +05:30
Anik Ghosh
b2541c8e9a feat: update user access (#151)
* feat: update user access

* revoked timestamp field updated

* updates

* updates

* updates
2022-03-24 14:13:55 +05:30
Lakhan Samani
1f3dec6ea6 feat: add validate_jwt_token query
Resolves #149
2022-03-24 13:32:30 +05:30
Lakhan Samani
a6b743465f feat: add provider template 2022-03-19 17:41:27 +05:30
Lakhan Samani
f356b4728d Merge pull request #142 from authorizerdev/feat/add-password-validation
feat: add validation for strong password
2022-03-19 11:31:31 +05:30
Lakhan Samani
ec4ef97766 feat: add validation for strong password 2022-03-17 15:35:07 +05:30
Lakhan Samani
47d67bf3cd fix: update readme.md 2022-03-17 09:33:55 +05:30
Lakhan Samani
0c54da1168 fix: getting started 2022-03-17 09:31:40 +05:30
Lakhan Samani
d6f60ce464 chore: add workflow-dispatch 2022-03-17 00:44:55 +05:30
Lakhan Samani
3aa888b14e fix: use latest authorizer-react 2022-03-17 00:28:11 +05:30
Lakhan Samani
30be32a10b feat: add sample csv 2022-03-17 00:15:47 +05:30
Lakhan Samani
69d781d6cf fix: set password re-direct uri 2022-03-17 00:04:57 +05:30
Lakhan Samani
e4d9c60971 Merge pull request #139 from anik-ghosh-au7/feat/disable-signup
feat: disable user signup
2022-03-16 23:18:43 +05:30
Anik Ghosh
96edb43b67 feat: disable user signup 2022-03-16 22:49:18 +05:30
Lakhan Samani
21fef67c7d Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-03-16 21:52:51 +05:30
Lakhan Samani
9f09823c8b feat: add redirect_uri for signup 2022-03-16 21:52:45 +05:30
Lakhan Samani
1a64149da7 Merge pull request #138 from anik-ghosh-au7/feat/invite-emails
Feat/invite emails
2022-03-16 21:45:34 +05:30
Lakhan Samani
99b846811a fix: token + redirect 2022-03-16 21:44:57 +05:30
Anik Ghosh
df7837f44d updates 2022-03-16 20:22:24 +05:30
Anik Ghosh
d709f53c47 updates 2022-03-16 20:13:18 +05:30
Anik Ghosh
a257b77501 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/invite-emails 2022-03-16 18:07:16 +05:30
Anik Ghosh
2213619ed5 updates 2022-03-16 18:06:51 +05:30
Anik Ghosh
f65ea72944 package-lock.json 2022-03-16 14:10:55 +05:30
Anik Ghosh
32f8c99a71 updates 2022-03-16 14:08:22 +05:30
Anik Ghosh
8ec52a90f1 updates 2022-03-16 14:08:08 +05:30
Anik Ghosh
2498958295 updates 2022-03-16 00:07:58 +05:30
Anik Ghosh
2913fa0603 updates 2022-03-15 23:51:54 +05:30
Anik Ghosh
e126bfddad invite email modal updated 2022-03-15 20:31:54 +05:30
Lakhan Samani
83001b859c Merge pull request #136 from authorizerdev/feat/invite-member
feat: add resolver for inviting members
2022-03-15 12:51:12 +05:30
Lakhan Samani
74a8024131 feat: add integration test for invite_member 2022-03-15 12:09:54 +05:30
Lakhan Samani
5e6ee8d9b0 fix: setup-password flow 2022-03-15 09:57:09 +05:30
Lakhan Samani
3e7150f872 fix: redirect uri 2022-03-15 09:56:50 +05:30
Lakhan Samani
9a19552f72 feat: add resolver for inviting members 2022-03-15 08:53:48 +05:30
Anik Ghosh
ab01ff249d invite email modal added 2022-03-15 01:24:14 +05:30
Lakhan Samani
1b387f7564 fix: getting version in meta api 2022-03-09 18:55:18 +05:30
Lakhan Samani
8e79ab77b2 Merge pull request #131 from authorizerdev/feat/open-id
Add open id authorization flow with PKCE
2022-03-09 17:27:16 +05:30
Lakhan Samani
2bf6b8f91d fix: remove log 2022-03-09 17:24:53 +05:30
Lakhan Samani
776c0fba8b chore: app dependencies 2022-03-09 17:21:55 +05:30
Lakhan Samani
dd64aa2e79 feat: add version info 2022-03-09 11:53:34 +05:30
Lakhan Samani
157b13baa7 fix: basic auth redirect 2022-03-09 10:10:39 +05:30
Lakhan Samani
d1e284116d fix: verification request model 2022-03-09 07:10:07 +05:30
Lakhan Samani
2f9725d8e1 fix: verification request 2022-03-09 06:41:38 +05:30
Lakhan Samani
ee7aea7bee fix: verify email 2022-03-08 22:55:45 +05:30
Lakhan Samani
5d73df0040 fix: magic link login 2022-03-08 22:41:33 +05:30
Lakhan Samani
60cd317e67 fix: add redirect url to logout 2022-03-08 21:32:42 +05:30
Lakhan Samani
f5bdc8db39 fix: refresh token store info 2022-03-08 21:13:23 +05:30
Lakhan Samani
9eca697a91 fix: refresh token param in string 2022-03-08 19:31:19 +05:30
Lakhan Samani
7136ee924d fix: rotate refresh token 2022-03-08 19:18:33 +05:30
Lakhan Samani
fd9eb7c733 fix: oauth state split 2022-03-08 19:13:45 +05:30
Lakhan Samani
917eaeb2ed feat: don't set cookie in case of offline_access 2022-03-08 18:51:46 +05:30
Lakhan Samani
3bb90acc9e feat: add revoke mutation + handler 2022-03-08 18:49:42 +05:30
Lakhan Samani
a69b8e290c feat: add ability to get access token based on refresh token 2022-03-08 14:56:46 +05:30
Lakhan Samani
674eeeea4e chore: bump authorizer-react 2022-03-08 14:20:11 +05:30
Lakhan Samani
8c2bf6ee0d fix: add token information in redirect url 2022-03-08 12:36:26 +05:30
Lakhan Samani
57bc091499 fix state management 2022-03-07 23:44:19 +05:30
Lakhan Samani
128a2a8f75 feat: add support for response mode 2022-03-07 18:49:18 +05:30
Lakhan Samani
7b09a8817c fix: env encryption 2022-03-07 16:16:54 +05:30
Lakhan Samani
1d61840c6d fix: env decryption + remove log 2022-03-07 15:35:33 +05:30
Lakhan Samani
4b25e8941c fix: env decryption 2022-03-07 15:33:39 +05:30
Lakhan Samani
136eda15bf fix: env encryption 2022-03-07 15:29:37 +05:30
Lakhan Samani
eea6349318 chore: update app version 2022-03-07 12:33:42 +05:30
Lakhan Samani
513b5d2948 fix: env client secret 2022-03-07 12:23:45 +05:30
Lakhan Samani
e61dc2f08a fix: oauth login 2022-03-07 08:31:39 +05:30
Lakhan Samani
07552bc0b1 fix: use url safe code verifier 2022-03-05 13:50:59 +05:30
Lakhan Samani
0787a3b494 feat: add token endpoint 2022-03-04 12:56:11 +05:30
Lakhan Samani
2946428ab8 feat: add userinfo + logout 2022-03-04 00:36:27 +05:30
Lakhan Samani
5c7d32ec16 fix: remove compat cookie 2022-03-03 09:21:48 +05:30
Lakhan Samani
f0f2e0b6c8 fix: auth flow 2022-03-02 17:42:31 +05:30
Lakhan Samani
5399ea8f32 feat: add session token 2022-02-28 21:26:49 +05:30
Lakhan Samani
4830a7e9ac feat: add client secret 2022-02-28 13:14:16 +05:30
Lakhan Samani
df1c56bb1c fix: tests 2022-02-28 07:55:01 +05:30
Lakhan Samani
b68d9ce661 fix: update_env resolver 2022-02-26 20:36:22 +05:30
Lakhan Samani
145091dce1 feat: add well-known jwks.json endpoint 2022-02-26 18:14:43 +05:30
Lakhan Samani
ad46210112 fix: report error on initialization 2022-02-26 10:06:26 +05:30
Lakhan Samani
4e19f73845 fix: segregate env setup 2022-02-26 09:44:55 +05:30
Lakhan Samani
332269ecf9 feat: add well-known config endpoint 2022-02-23 11:24:52 +05:30
Lakhan Samani
dfa96f09a0 feat: add required jwt claims 2022-02-22 11:06:47 +05:30
Lakhan Samani
5bf26f7385 fix: setting custom access token script env 2022-02-18 16:45:12 +05:30
Lakhan Samani
1b269dc6db fix: rename session_client -> redis_client 2022-02-18 09:21:02 +05:30
Lakhan Samani
ce9a115a14 Merge pull request #128 from agarwal-nitesh/feat/redis_cluster_client
Add redis cluster client as a session store.
2022-02-18 09:19:24 +05:30
Nitesh Agarwal
f2f4c72aa6 Add redis cluster client as a session store. 2022-02-17 20:49:54 +05:30
Samyak Bhuta
9970eb16c9 Update README
Minor changes.
2022-02-17 15:04:05 +05:30
Lakhan Samani
23e53286bd Merge pull request #124 from anik-ghosh-au7/feat/jwt-types
support for more jwt encryption types added
2022-02-14 18:56:06 +05:30
Anik Ghosh
47acff05e2 support for more jwt encryption types added 2022-02-14 16:00:35 +05:30
Lakhan Samani
5572928619 fix: remove redundunt break statement 2022-02-12 22:55:33 +05:30
Lakhan Samani
85b4cd6339 Add support for maria db 2022-02-12 22:49:53 +05:30
Lakhan Samani
f0d38ab260 Merge pull request #121 from authorizerdev/feat/add-jwt-algos
feat: add jwt algos
2022-02-12 19:37:28 +05:30
Lakhan Samani
1276af43ef Add new line char 2022-02-12 19:36:29 +05:30
Lakhan Samani
66d42fc2bc Add support for public private key from admin apis 2022-02-12 19:34:22 +05:30
Lakhan Samani
1f058f954d Add test for jwt tokens 2022-02-12 19:26:37 +05:30
Lakhan Samani
8259fb515c Add support for more JWT algo methods 2022-02-12 15:54:23 +05:30
Lakhan Samani
6c2a4c3256 Add support for yugabyte
Resolves #119
2022-02-12 13:19:31 +05:30
Lakhan Samani
51532657d7 fix: input labels 2022-02-07 21:15:08 +05:30
Lakhan Samani
fd56fac353 fix: add custom access token script to env sample 2022-02-07 09:42:49 +05:30
Lakhan Samani
260f533b41 fix: resolves #115 2022-02-05 10:09:25 +05:30
Lakhan Samani
c09ca3b810 fix: improve messaging on dashboard 2022-02-05 09:15:36 +05:30
Lakhan Samani
f07fb50eff Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-02-05 09:01:22 +05:30
Lakhan Samani
ea62a20c80 fix: remove test env 2022-02-05 09:01:12 +05:30
Lakhan Samani
2bb0ded20e fix: error log and gin mode 2022-02-05 09:00:56 +05:30
Lakhan Samani
fec43f55f2 chore: update project setup info 2022-02-02 13:18:26 +05:30
Lakhan Samani
271e901398 chore: update project setup info 2022-02-02 13:17:35 +05:30
Lakhan Samani
c6f01ce839 chore: update project setup info in contributing 2022-02-02 13:15:54 +05:30
Lakhan Samani
2dd404c02c chore: update project setup info 2022-02-02 13:15:14 +05:30
Lakhan Samani
2f29bbcee4 fix: oauth callback update user 2022-02-02 12:30:11 +05:30
Lakhan Samani
63a8c82535 chore: update package-lock.json 2022-02-02 11:41:36 +05:30
Lakhan Samani
1f81e45e79 fix: user dashboard created_at + add signup_methods & roles 2022-02-02 11:39:08 +05:30
Lakhan Samani
40dcf67de9 fix: remove logs 2022-01-31 14:53:17 +05:30
Lakhan Samani
32d8b7c038 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-01-31 14:30:54 +05:30
Lakhan Samani
2a91f3e7d8 Merge pull request #113 from anik-ghosh-au7/fix/dashboard
Fix/dashboard
2022-01-31 14:30:32 +05:30
Lakhan Samani
36d9861517 Fix getting host 2022-01-31 14:30:13 +05:30
Anik Ghosh
d577a21a9a user dashboard pagination bug removed 2022-01-31 14:26:19 +05:30
Anik Ghosh
115607cb6b Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/dashboard 2022-01-31 13:29:38 +05:30
Anik Ghosh
adb969ec04 add delete user modal 2022-01-31 13:23:38 +05:30
Lakhan Samani
4e48320cf1 fix: bug with authorizer url 2022-01-31 11:35:24 +05:30
Anik Ghosh
cfe035e96b loader added to user dashboard 2022-01-31 11:33:35 +05:30
Lakhan Samani
34a91f3195 Add log for host url 2022-01-30 23:44:37 +05:30
Lakhan Samani
ea14cc1743 Merge pull request #111 from anik-ghosh-au7/feat/manage-users
Feat/manage users
2022-01-30 23:43:21 +05:30
Anik Ghosh
9d7f5fd9db array input type update 2022-01-30 17:43:38 +05:30
Anik Ghosh
0520056e43 sync with main branch 2022-01-30 10:56:56 +05:30
Anik Ghosh
1821e27692 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/manage-users 2022-01-30 10:52:35 +05:30
Anik Ghosh
388530a69c edit user details modal added 2022-01-30 10:39:35 +05:30
Lakhan Samani
1e32d790b3 Remove unsed code 2022-01-30 00:08:51 +05:30
Anik Ghosh
681ffc65f1 user list added 2022-01-29 20:53:53 +05:30
Lakhan Samani
6331ec7b7a Resolves #110 2022-01-29 17:03:21 +05:30
Lakhan Samani
25c9ce03bd fix: default logo 2022-01-27 09:55:05 +05:30
Lakhan Samani
ac416bfc7b fix(dashboard): mutation 2022-01-25 13:06:52 +05:30
Lakhan Samani
0049e1380b Merge pull request #108 from authorizerdev/feat/pagination
feat: add pagination for users & verification_requests
2022-01-25 10:58:42 +05:30
Lakhan Samani
9bd185a9c6 feat: add pagination for users & verification_requests 2022-01-25 10:57:40 +05:30
Lakhan Samani
82bc38923c Feat/manage env vars (#107)
* ui to manage env variables added

* ui to manage env variables updated

* ui to manage env variables updated

* ui to manage env variables updated

* ui to manage env variables updated

* ui updates

* ui updates

* ui updates

* ui updates

* env vars input field updated

* env vars input field updated

Co-authored-by: Anik Ghosh <tech.anikghosh@gmail.com>
2022-01-25 09:34:35 +05:30
Lakhan Samani
8df8010b22 Fix SMTP fields for update env mutation 2022-01-24 21:16:29 +05:30
Lakhan Samani
b42cc1549a fix: env to return custom access token script 2022-01-24 10:22:55 +05:30
Lakhan Samani
4bc9059b0f fix: allow using cookie and header in case of validating jwt 2022-01-24 09:56:12 +05:30
Lakhan Samani
87b1cac979 feat(server): add is_valid_jwt query 2022-01-24 00:32:06 +05:30
Lakhan Samani
7f18a3f634 Implement refresh token logic with fingerprint + rotation 2022-01-23 01:24:41 +05:30
Lakhan Samani
0511e737ae fix(server): add update roles env validation 2022-01-22 11:29:03 +05:30
Lakhan Samani
003d88fb6c Merge pull request #106 from authorizerdev/fix/organize_dbs
fix: organize dbs
2022-01-21 13:37:37 +05:30
Lakhan Samani
515b72f484 fix: cleaning of test 2022-01-21 13:36:19 +05:30
Lakhan Samani
cb96d2d8d1 fix: update to use db.Provider 2022-01-21 13:34:04 +05:30
Lakhan Samani
8a4b2feffe fix: user + verification requests to new db format 2022-01-21 12:53:30 +05:30
Lakhan Samani
13c038effd fix: env + session to new db format 2022-01-21 12:18:07 +05:30
Lakhan Samani
38419a4ef4 fix: rename config -> env and handle env interface better 2022-01-20 16:52:37 +05:30
Lakhan Samani
7785f98dcd fix(server): env setup 2022-01-19 23:19:20 +05:30
Lakhan Samani
5ecc49f861 fix(doc): merge conflict in contributing 2022-01-19 22:47:06 +05:30
Lakhan Samani
3cb02dd62c fix(dashboard): update home page text 2022-01-19 22:43:07 +05:30
Lakhan Samani
ddda237178 fix(dashboard): layout 2022-01-19 22:20:25 +05:30
Lakhan Samani
3b4d0d9769 fix(server): add old secret check for admin secret update 2022-01-17 13:20:32 +05:30
Lakhan Samani
c15b65b473 fix(server): rename config -> env 2022-01-17 13:12:46 +05:30
Lakhan Samani
e07448f670 fix(dashboard): remove unused var 2022-01-17 13:03:57 +05:30
Lakhan Samani
a596d91ce0 fix(dashboard): navigation issues 2022-01-17 13:03:28 +05:30
Lakhan Samani
f1b4141367 Feat/dashboard (#105) 2022-01-17 11:32:13 +05:30
Lakhan Samani
7ce96367a3 Merge pull request #104 from jyash97/yash/dashboard
feat: setup authorizer dashboard
2022-01-17 11:01:19 +05:30
Lakhan Samani
974622b9be Merge branch 'main' into yash/dashboard 2022-01-17 11:00:54 +05:30
Yash Joshi
8bee841d66 feat: setup dashboard
- Setup basic code structure
- Add routes
- Add layout components for authentication and dashboard pages
- Add session handling
- Add login, signup and session
2022-01-15 21:15:46 +05:30
Lakhan Samani
9363d83945 Update contributing test doc 2022-01-14 11:59:11 +05:30
Lakhan Samani
75affcbf30 Update contributing test doc 2022-01-14 11:59:11 +05:30
Lakhan Samani
f5aeda1283 Update arangodb test connection string 2022-01-14 11:59:11 +05:30
Lakhan Samani
f9ed91934e Update response + input types for admin apis 2022-01-09 18:40:30 +05:30
Lakhan Samani
266b9e34b6 Remove access_token from response 2022-01-09 18:02:16 +05:30
Lakhan Samani
047e867faa Add admin session only via http cookies 2022-01-09 17:40:08 +05:30
Lakhan Samani
9d08d6c672 Add _admin_signup mutation 2022-01-09 17:35:37 +05:30
Lakhan Samani
91c35aa381 Merge branch 'main' into feat/dashboard 2022-01-09 15:04:08 +05:30
Lakhan Samani
8e85d0ddbd Update test 2022-01-08 19:02:00 +05:30
Lakhan Samani
bb4052d1d2 Merge branch 'main' into feat/dashboard 2022-01-08 18:46:44 +05:30
Lakhan Samani
e0ae6aa2e0 Update dashboard 2022-01-08 15:46:39 +05:30
Lakhan Samani
ca716ec1dd Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/dashboard 2022-01-08 11:43:12 +05:30
Lakhan Samani
818790650a fix: enable test for mongo & arango 2022-01-01 08:41:44 +05:30
Lakhan Samani
eb5041008d Merge pull request #93 from authorizerdev/feat/admin-logout
Feat/admin logout
2021-12-31 23:07:41 +05:30
Lakhan Samani
152ab6dfd5 feat: add admin logout 2021-12-31 23:06:06 +05:30
Lakhan Samani
192070c18e Merge pull request #92 from authorizerdev/feat/setup-onboard-apis
feat/setup onboard apis
2021-12-31 17:27:22 +05:30
Lakhan Samani
f7f1a3e4b3 feat: add api for getting configurations 2021-12-31 17:24:22 +05:30
Lakhan Samani
9c8e9baa39 feat: add _update_config mutation 2021-12-31 17:03:37 +05:30
Lakhan Samani
217410e9a4 feat: add admin session api 2021-12-31 14:28:00 +05:30
Lakhan Samani
e35d0cbcd6 feat: persist encrypted env 2021-12-31 13:52:10 +05:30
Lakhan Samani
d9c40057e6 feat: add api for admin login 2021-12-30 10:01:51 +05:30
Lakhan Samani
86bcb8ca87 Merge pull request #91 from authorizerdev/feat/dashboard-setup
feat: add dashboard setup with esbuild + chakra-ui
2021-12-29 12:05:34 +05:30
Lakhan Samani
cf4d94a7aa feat: add dashboard setup with esbuild + chakra-ui 2021-12-29 11:56:19 +05:30
437 changed files with 63303 additions and 7305 deletions

View File

@@ -9,3 +9,4 @@ build
data.db data.db
app/node_modules app/node_modules
app/build app/build
certs/

View File

@@ -1,16 +1,5 @@
ENV=production ENV=production
DATABASE_URL=data.db DATABASE_URL=data.db
DATABASE_TYPE=sqlite DATABASE_TYPE=sqlite
ADMIN_SECRET=admin CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
JWT_SECRET=random_string DISABLE_PLAYGROUND=true
SENDER_EMAIL=info@authorizer.dev
SMTP_USERNAME=username
SMTP_PASSWORD=password
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
JWT_TYPE=HS256
ROLES=user
DEFAULT_ROLES=user
PROTECTED_ROLES=admin
JWT_ROLE_CLAIM=role
CUSTOM_ACCESS_TOKEN_SCRIPT=function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}

15
.env.test Normal file
View File

@@ -0,0 +1,15 @@
ENV=test
DATABASE_URL=test.db
DATABASE_TYPE=sqlite
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USERNAME=test
SMTP_PASSWORD=test
SENDER_EMAIL="info@authorizer.dev"
TWILIO_API_KEY=test
TWILIO_API_SECRET=test
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
TWILIO_SENDER=909921212112
SENDER_NAME="Authorizer"
AWS_REGION=ap-south-1

35
.gitea/workflows/main.yml Normal file
View File

@@ -0,0 +1,35 @@
name: "deploy"
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Cloning repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get Repo Name
id: repo_name
run: echo "::set-output name=repo::$(echo ${GITHUB_REPOSITORY##*/})"
- name: Get Branch Name
id: branch_name
run: echo "::set-output name=branch::$(echo ${GITHUB_REF##*/})"
- name: Push branch 'discours-dev' to staging
if: steps.branch_name.outputs.branch == 'discours-dev'
uses: dokku/github-action@master
with:
branch: "main"
git_remote_url: "ssh://dokku@staging.discours.io:22/authorizer"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Push branch 'discours' to v2.discours.io
if: steps.branch_name.outputs.branch == 'discours'
uses: dokku/github-action@master
with:
branch: "main"
git_remote_url: "ssh://dokku@v2.discours.io:22/authorizer"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

View File

@@ -43,12 +43,32 @@ Please ask as many questions as you need, either directly in the issue or on [Di
### Project Setup for Authorizer core ### Project Setup for Authorizer core
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**) 1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
2. `git clone https://github.com/authorizerdev/authorizer.git` 2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1
3. `cd authorizer` 3. Change directory to authorizer: `cd authorizer`
4. `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/) 4. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
5. Build the code `make clean && make` 5. Build Dashboard `make build-dashboard`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command 6. Build App `make build-app`
6. Run binary `./build/server` 7. Build Server `make clean && make`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command. In that case you will have to build `dashboard` & `app` manually using `npm run build` on both dirs.
8. Run binary `./build/server`
### Updating GraphQL schema
- Modify `server/graph/schema.graphqls` file
- Run `make generate-graphql` this will update the models and required methods
- If a new mutation or query is added
- Write the implementation for the new resolver in `server/resolvers/NEW_RESOLVER.GO`
- Update `server/graph/schema.resolvers.go` with the new resolver method
### Adding support for new database
- Run `make generate-db-template dbname=NEW_DB_NAME`
eg `make generate-db-template dbname=dynamodb`
This command will generate a folder in server/db/providers/ with name specified in the above command.
One will have to implement methods present in that folder.
> Note: Connection for database and schema changes are written in `server/db/providers/DB_NAME/provider.go` > `NewProvider` method is called for any given db based on the env variables present.
### Testing ### Testing
@@ -60,33 +80,39 @@ Setup mongodb & arangodb using Docker
``` ```
docker run --name mongodb -d -p 27017:27017 mongo docker run --name mongodb -d -p 27017:27017 mongo
docker run --name arangodb -d -p 8529:8529 -e ARANGO_ROOT_PASSWORD=root arangodb/arangodb:3.8.4
// -e ARANGO_ROOT_PASSWORD=root
docker run --name arangodb -d -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
``` ```
> Note: If you are not making any changes in db schema / db operations, you can disable those db tests [here](https://github.com/authorizerdev/authorizer/blob/main/server/__test__/resolvers_test.go#L14) > Note: If you are not making any changes in db schema / db operations, you can disable those db tests [here](https://github.com/authorizerdev/authorizer/blob/main/server/__test__/resolvers_test.go#L14)
If you are adding new resolver, If you are adding new resolver,
1. create new resolver test file [here](https://github.com/authorizerdev/authorizer/tree/main/server/__test__) 1. create new resolver test file [here](https://github.com/authorizerdev/authorizer/tree/main/server/__test__)
Naming convention filename: `resolver_name_test.go` function name: `resolverNameTest(s TestSetup, t *testing.T)` Naming convention filename: `resolver_name_test.go` function name: `resolverNameTest(t *testing.T, s TestSetup)`
2. Add your tests [here](https://github.com/authorizerdev/authorizer/blob/main/server/__test__/resolvers_test.go#L38) 2. Add your tests [here](https://github.com/authorizerdev/authorizer/blob/main/server/__test__/resolvers_test.go#L38)
__Command to run tests:__ **Command to run tests:**
```sh ```sh
make test make test
``` ```
__Manual Testing:__ **Manual Testing:**
For manually testing using graphql playground, you can paste following queries and mutations in your playground and test it For manually testing using graphql playground, you can paste following queries and mutations in your playground and test it
```gql ```gql
mutation Signup { mutation Signup {
signup(params: { signup(
email: "lakhan@yopmail.com", params: {
password: "test", email: "lakhan@yopmail.com"
confirm_password: "test", password: "test"
given_name: "lakhan" confirm_password: "test"
}) { given_name: "lakhan"
}
) {
message message
user { user {
id id
@@ -99,10 +125,9 @@ mutation Signup {
} }
mutation ResendEamil { mutation ResendEamil {
resend_verify_email(params: { resend_verify_email(
email: "lakhan@yopmail.com" params: { email: "lakhan@yopmail.com", identifier: "basic_auth_signup" }
identifier: "basic_auth_signup" ) {
}) {
message message
} }
} }
@@ -117,9 +142,7 @@ query GetVerifyRequests {
} }
mutation VerifyEmail { mutation VerifyEmail {
verify_email(params: { verify_email(params: { token: "" }) {
token: ""
}) {
access_token access_token
expires_at expires_at
user { user {
@@ -132,10 +155,7 @@ mutation VerifyEmail {
} }
mutation Login { mutation Login {
login(params: { login(params: { email: "lakhan@yopmail.com", password: "test" }) {
email: "lakhan@yopmail.com",
password: "test"
}) {
access_token access_token
expires_at expires_at
user { user {
@@ -165,27 +185,21 @@ query GetSession {
} }
mutation ForgotPassword { mutation ForgotPassword {
forgot_password(params: { forgot_password(params: { email: "lakhan@yopmail.com" }) {
email: "lakhan@yopmail.com"
}) {
message message
} }
} }
mutation ResetPassword { mutation ResetPassword {
reset_password(params: { reset_password(
token: "" params: { token: "", password: "test", confirm_password: "test" }
password: "test" ) {
confirm_password: "test"
}) {
message message
} }
} }
mutation UpdateProfile { mutation UpdateProfile {
update_profile(params: { update_profile(params: { family_name: "samani" }) {
family_name: "samani"
}) {
message message
} }
} }
@@ -204,9 +218,7 @@ query GetUsers {
} }
mutation MagicLinkLogin { mutation MagicLinkLogin {
magic_link_login(params: { magic_link_login(params: { email: "test@yopmail.com" }) {
email: "test@yopmail.com"
}) {
message message
} }
} }
@@ -217,24 +229,21 @@ mutation Logout {
} }
} }
mutation UpdateUser{ mutation UpdateUser {
_update_user(params: { _update_user(
id: "dafc9400-d603-4ade-997c-83fcd54bbd67", params: {
roles: ["user", "admin"] id: "dafc9400-d603-4ade-997c-83fcd54bbd67"
}) { roles: ["user", "admin"]
}
) {
email email
roles roles
} }
} }
mutation DeleteUser { mutation DeleteUser {
_delete_user(params: { _delete_user(params: { email: "signup.test134523@yopmail.com" }) {
email: "signup.test134523@yopmail.com"
}) {
message message
} }
} }
``` ```

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: authorizerdev

View File

@@ -1,52 +1,79 @@
on: on:
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
required: true
default: 'warning'
type: choice
options:
- info
- warning
- debug
tags:
description: 'Tags'
required: false
type: boolean
release: release:
types: [created] types: [created]
jobs: jobs:
releases: releases:
name: Release Authorizer Binary name: Release Authorizer
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: actions/setup-node@v2 - uses: actions/setup-node@v2
with: with:
node-version: '16' node-version: '16'
- # Add support for more platforms with QEMU (optional)
# https://github.com/docker/setup-qemu-action
name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
platforms: linux/amd64,linux/arm64
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
with: with:
go-version: '^1.17.3' go-version: '^1.19.1'
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get install build-essential wget zip gcc-mingw-w64 && \ sudo apt-get install build-essential wget zip libc6-dev-arm64-cross && \
echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH && \ echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH && \
wget --no-check-certificate --progress=dot:mega https://github.com/wangyoucao577/assets-uploader/releases/download/v0.3.0/github-assets-uploader-v0.3.0-linux-amd64.tar.gz -O github-assets-uploader.tar.gz && \ wget --no-check-certificate --progress=dot:mega https://github.com/wangyoucao577/assets-uploader/releases/download/v0.3.0/github-assets-uploader-v0.3.0-linux-amd64.tar.gz -O github-assets-uploader.tar.gz && \
tar -zxf github-assets-uploader.tar.gz && \ tar -zxf github-assets-uploader.tar.gz && \
sudo mv github-assets-uploader /usr/sbin/ && \ sudo mv github-assets-uploader /usr/sbin/ && \
sudo rm -f github-assets-uploader.tar.gz && \ sudo rm -f github-assets-uploader.tar.gz && \
github-assets-uploader -version && \ github-assets-uploader -version && \
make build-app make build-app && \
make build-dashboard
- name: Print Go paths - name: Print Go paths
run: whereis go run: whereis go
- name: Print Go Version - name: Print Go Version
run: go version run: go version
- name: Install gox
run: go install github.com/mitchellh/gox@latest
- name: Set VERSION env - name: Set VERSION env
run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV} run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV}
- name: Copy .env file - name: Copy .env file
run: mv .env.sample .env run: mv .env.sample .env
- name: Package files for windows - name: Build package
run: | run: |
make clean && \ make clean && \
CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \ make build && \
mv build/server build/server.exe && \ mkdir -p authorizer-${VERSION}-darwin-arm64/build authorizer-${VERSION}-darwin-arm64/app authorizer-${VERSION}-darwin-arm64/dashboard && cp build/darwin/arm64/server authorizer-${VERSION}-darwin-arm64/build/ && cp .env authorizer-${VERSION}-darwin-arm64/.env && cp -rf app/build authorizer-${VERSION}-darwin-arm64/app/build && cp -rf templates authorizer-${VERSION}-darwin-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-arm64.tar.gz authorizer-${VERSION}-darwin-arm64 && \
zip -vr authorizer-${VERSION}-windows-amd64.zip .env app/build build templates mkdir -p authorizer-${VERSION}-darwin-amd64/build authorizer-${VERSION}-darwin-amd64/app authorizer-${VERSION}-darwin-amd64/dashboard && cp build/darwin/amd64/server authorizer-${VERSION}-darwin-amd64/build/ && cp .env authorizer-${VERSION}-darwin-amd64/.env && cp -rf app/build authorizer-${VERSION}-darwin-amd64/app/build && cp -rf templates authorizer-${VERSION}-darwin-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-amd64.tar.gz authorizer-${VERSION}-darwin-amd64 && \
- name: Package files for linux mkdir -p authorizer-${VERSION}-linux-amd64/build authorizer-${VERSION}-linux-amd64/app authorizer-${VERSION}-linux-amd64/dashboard && cp build/linux/amd64/server authorizer-${VERSION}-linux-amd64/build/ && cp .env authorizer-${VERSION}-linux-amd64/.env && cp -rf app/build authorizer-${VERSION}-linux-amd64/app/build && cp -rf templates authorizer-${VERSION}-linux-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz authorizer-${VERSION}-linux-amd64 && \
run: | mkdir -p authorizer-${VERSION}-linux-arm64/build authorizer-${VERSION}-linux-arm64/app authorizer-${VERSION}-linux-arm64/dashboard && cp build/linux/arm64/server authorizer-${VERSION}-linux-arm64/build/ && cp .env authorizer-${VERSION}-linux-arm64/.env && cp -rf app/build authorizer-${VERSION}-linux-arm64/app/build && cp -rf templates authorizer-${VERSION}-linux-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-arm64.tar.gz authorizer-${VERSION}-linux-arm64 && \
make clean && \ mkdir -p authorizer-${VERSION}-windows-amd64/build authorizer-${VERSION}-windows-amd64/app authorizer-${VERSION}-windows-amd64/dashboard && cp build/windows/amd64/server.exe authorizer-${VERSION}-windows-amd64/build/ && cp .env authorizer-${VERSION}-windows-amd64/.env && cp -rf app/build authorizer-${VERSION}-windows-amd64/app/build && cp -rf templates authorizer-${VERSION}-windows-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-windows-amd64/dashboard/build && zip -vr authorizer-${VERSION}-windows-amd64.zip authorizer-${VERSION}-windows-amd64
CGO_ENABLED=1 make && \
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app/build build templates
- name: Upload assets - name: Upload assets
run: | run: |
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} && \ github-assets-uploader -f authorizer-${VERSION}-darwin-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-darwin-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-linux-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v1 uses: docker/login-action@v1
with: with:
@@ -58,6 +85,11 @@ jobs:
uses: docker/metadata-action@v3 uses: docker/metadata-action@v3
with: with:
images: lakhansamani/authorizer images: lakhansamani/authorizer
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v2 uses: docker/build-push-action@v2
@@ -66,5 +98,6 @@ jobs:
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
build-args: | build-args: |
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }} VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}

13
.gitignore vendored
View File

@@ -3,9 +3,20 @@ server/.env
data data
app/node_modules app/node_modules
app/build app/build
dashboard/node_modules
dashboard/build
build build
.env .env
data.db data.db
test.db
.DS_Store .DS_Store
.env.local .env.local
*.tar.gz *.tar.gz
.vscode/
.yalc
yalc.lock
certs/
*-shm
*-wal
.idea
*.iml

View File

@@ -1,4 +1,4 @@
FROM golang:1.17-alpine as go-builder FROM golang:1.21.3-alpine3.18 as go-builder
WORKDIR /authorizer WORKDIR /authorizer
COPY server server COPY server server
COPY Makefile . COPY Makefile .
@@ -11,18 +11,25 @@ RUN apk add build-base &&\
make clean && make && \ make clean && make && \
chmod 777 build/server chmod 777 build/server
FROM node:17-alpine3.12 as node-builder FROM node:20-alpine3.18 as node-builder
WORKDIR /authorizer WORKDIR /authorizer
COPY app app COPY app app
COPY dashboard dashboard
COPY Makefile . COPY Makefile .
RUN apk add build-base &&\ RUN apk add build-base &&\
make build-app make build-app && \
make build-dashboard
FROM alpine:latest FROM alpine:3.18
WORKDIR /root/ RUN adduser -D -h /authorizer -u 1000 -k /dev/null authorizer
RUN mkdir app WORKDIR /authorizer
COPY --from=node-builder /authorizer/app/build app/build RUN mkdir app dashboard
COPY --from=go-builder /authorizer/build build COPY --from=node-builder --chown=nobody:nobody /authorizer/app/build app/build
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/favicon_io app/favicon_io
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/build dashboard/build
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/favicon_io dashboard/favicon_io
COPY --from=go-builder --chown=nobody:nobody /authorizer/build build
COPY templates templates COPY templates templates
EXPOSE 8080 EXPOSE 8080
USER authorizer
CMD [ "./build/server" ] CMD [ "./build/server" ]

View File

@@ -3,9 +3,57 @@ VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
cmd: cmd:
cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server' cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server'
build:
cd server && gox \
-osarch="linux/amd64 linux/arm64 darwin/arm64 darwin/amd64 windows/amd64" \
-ldflags "-w -X main.VERSION=$(VERSION)" \
-output="../build/{{.OS}}/{{.Arch}}/server" \
./...
build-app: build-app:
cd app && npm i && npm run build cd app && npm i && npm run build
build-dashboard:
cd dashboard && npm i && npm run build
clean: clean:
rm -rf build rm -rf build
test: test:
cd server && go clean --testcache && go test -v ./__test__ rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test
test-mongodb:
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
cd server && go clean --testcache && TEST_DBS="mongodb" go test -p 1 -v ./test
docker rm -vf authorizer_mongodb_db
test-scylladb:
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
cd server && go clean --testcache && TEST_DBS="scylladb" go test -p 1 -v ./test
docker rm -vf authorizer_scylla_db
test-arangodb:
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
docker rm -vf authorizer_arangodb
test-dynamodb:
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
cd server && go clean --testcache && TEST_DBS="dynamodb" go test -p 1 -v ./test
docker rm -vf dynamodb-local-test
test-couchbase:
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
sh scripts/couchbase-test.sh
cd server && go clean --testcache && TEST_DBS="couchbase" go test -p 1 -v ./test
docker rm -vf couchbase-local-test
test-all-db:
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
sh scripts/couchbase-test.sh
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb,dynamodb,couchbase" go test -p 1 -v ./test
docker rm -vf authorizer_scylla_db
docker rm -vf authorizer_mongodb_db
docker rm -vf authorizer_arangodb
docker rm -vf dynamodb-local-test
docker rm -vf couchbase-local-test
generate-graphql:
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
generate-db-template:
cp -rf server/db/providers/provider_template server/db/providers/${dbname}
find server/db/providers/${dbname} -type f -exec sed -i -e 's/provider_template/${dbname}/g' {} \;

123
README.md
View File

@@ -1,50 +1,48 @@
<p align="center"> <p align="center">
<a href="https://authorizer.dev"> <a href="https://authorizer.dev">
<img alt="Logo" src="https://github.com/authorizerdev/authorizer/blob/main/assets/logo.png" width="60" /> <img alt="Logo" src="https://authorizer.dev/images/logo.png" width="60" />
</a> </a>
</p> </p>
<h1 align="center"> <h1 align="center">
Authorizer Authorizer
</h1> </h1>
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [MongoDB](https://mongodb.com/),[ArangoDB](https://www.arangodb.com/)). **Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports 11+ databases including [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [YugaByte](https://www.yugabyte.com/), [MariaDB](https://mariadb.org/), [PlanetScale](https://planetscale.com/), [CassandraDB](https://cassandra.apache.org/_/index.html), [ScyllaDB](https://www.scylladb.com/), [MongoDB](https://mongodb.com/), [ArangoDB](https://www.arangodb.com/)).
## Table of contents For more information check:
- [Introduction](#introduction)
- [Getting Started](#getting-started)
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
- [Docs](http://docs.authorizer.dev/) - [Docs](http://docs.authorizer.dev/)
- [Join Community](https://discord.gg/Zv2D5h6kkK) - [Discord Community](https://discord.gg/Zv2D5h6kkK)
- [Contributing Guide](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
# Introduction # Introduction
<img src="https://github.com/authorizerdev/authorizer/blob/main/assets/authorizer-architecture.png" style="height:20em"/> <img src="https://docs.authorizer.dev/images/authorizer-arch.png" style="height:20em"/>
#### We offer the following functionality #### We offer the following functionality
- ✅ Sign-in / Sign-up with email ID and password - ✅ Sign-in / Sign-up with email ID and password
- ✅ Secure session management - ✅ Secure session management
- ✅ Email verification - ✅ Email verification
- ✅ OAuth2 and OpenID compatible APIs
- ✅ APIs to update profile securely - ✅ APIs to update profile securely
- ✅ Forgot password flow using email - ✅ Forgot password flow using email
- ✅ Social logins (Google, Github, Facebook, more coming soon) - ✅ Social logins (Google, Github, Facebook, LinkedIn, Apple more coming soon)
- ✅ Role-based access management - ✅ Role-based access management
- ✅ Password-less login with email and magic link - ✅ Password-less login with magic link login
- ✅ Multi factor authentication
- ✅ Email templating
- ✅ Webhooks
## Roadmap ## Roadmap
- Support more JWT encryption algorithms (Currently supporting HS256) - [VueJS SDK](https://github.com/authorizerdev/authorizer-vue)
- 2 Factor authentication - [Svelte SDK](https://github.com/authorizerdev/authorizer-svelte)
- Back office (Admin dashboard to manage user) - [Golang SDK](https://github.com/authorizerdev/authorizer-go)
- Support more database
- VueJS SDK
- Svelte SDK
- React Native SDK - React Native SDK
- Flutter SDK - Flutter SDK
- Android Native SDK - Android Native SDK
- iOS native SDK - iOS native SDK
- Golang SDK
- Python SDK - Python SDK
- PHP SDK - PHP SDK
- WordPress plugin - WordPress plugin
@@ -59,33 +57,44 @@
# Getting Started # Getting Started
## Trying out Authorizer ## Step 1: Get Authorizer Instance
### Deploy Production Ready Instance
Deploy production ready Authorizer instance using one click deployment options available below
| **Infra provider** | **One-click link** | **Additional information** |
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
| Railway.app | <a href="https://railway.app/new/template/nwXp1C?referralCode=FEF4uT"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) |
| Heroku | <a href="https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy to Heroku" style="height: 44px;"></a> | [docs](https://docs.authorizer.dev/deployment/heroku) |
| Render | [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
| Koyeb | <a target="_blank" href="https://app.koyeb.com/deploy?name=authorizer&type=docker&image=docker.io/lakhansamani/authorizer&env[PORT]=8000&env[DATABASE_TYPE]=postgres&env[DATABASE_URL]=CHANGE_ME&ports=8000;http;/"><img alt="Deploy to Koyeb" src="https://www.koyeb.com/static/images/deploy/button.svg" /></a> | [docs](https://docs.authorizer.dev/deployment/koyeb) |
| RepoCloud | <a href="https://repocloud.io/details/?app_id=174"><img src="https://d16t0pc4846x52.cloudfront.net/deploy.png" alt="Deploy on RepoCloud"></a> | [docs](https://repocloud.io/details/?app_id=174) |
### Deploy Authorizer Using Source Code
This guide helps you practice using Authorizer to evaluate it before you use it in a production environment. It includes instructions for installing the Authorizer server in local or standalone mode. This guide helps you practice using Authorizer to evaluate it before you use it in a production environment. It includes instructions for installing the Authorizer server in local or standalone mode.
- [Install using source code](#install-using-source-code) #### Install using source code
- [Install using binaries](#install-using-binaries)
- [Install instance on heroku](#install-instance-on-Heroku)
- [Install instance on railway.app](#install-instance-on-railway)
## Install using source code #### Prerequisites
### Prerequisites
- OS: Linux or macOS or windows - OS: Linux or macOS or windows
- Go: (Golang)(https://golang.org/dl/) >= v1.15 - Go: (Golang)(https://golang.org/dl/) >= v1.15
### Project Setup #### Project Setup
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**) 1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
2. `git clone https://github.com/authorizerdev/authorizer.git` 2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1
3. `cd authorizer` 3. Change directory to authorizer: `cd authorizer`
4. `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/) 4. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
5. Build the code `make clean && make` 5. Build Dashboard `make build-dashboard`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command 6. Build App `make build-app`
6. Run binary `./build/server` 7. Build Server `make clean && make`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command. In that case you will have to build `dashboard` & `app` manually using `npm run build` on both dirs.
8. Run binary `./build/server`
## Install using binaries ### Deploy Authorizer using binaries
Deploy / Try Authorizer using binaries. With each [Authorizer Release](https://github.com/authorizerdev/authorizer/releases) Deploy / Try Authorizer using binaries. With each [Authorizer Release](https://github.com/authorizerdev/authorizer/releases)
binaries are baked with required deployment files and bundled. You can download a specific version of it for the following operating systems: binaries are baked with required deployment files and bundled. You can download a specific version of it for the following operating systems:
@@ -93,7 +102,7 @@ binaries are baked with required deployment files and bundled. You can download
- Mac OSX - Mac OSX
- Linux - Linux
### Step 1: Download and unzip bundle #### Download and unzip bundle
- Download the Bundle for the specific OS from the [release page](https://github.com/authorizerdev/authorizer/releases) - Download the Bundle for the specific OS from the [release page](https://github.com/authorizerdev/authorizer/releases)
@@ -113,11 +122,7 @@ binaries are baked with required deployment files and bundled. You can download
cd authorizer cd authorizer
``` ```
### Step 2: Configure environment variables #### Step 3: Start Authorizer
Required environment variables are pre-configured in `.env` file. But based on the production requirements, please configure more environment variables. You can refer to [environment variables docs](/core/env) for more information.
### Step 3: Start Authorizer
- Run following command to start authorizer - Run following command to start authorizer
@@ -129,25 +134,20 @@ Required environment variables are pre-configured in `.env` file. But based on t
> Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server` > Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server`
## Install instance on Heroku ## Step 2: Setup Instance
Deploy Authorizer using [heroku](https://github.com/authorizerdev/authorizer-heroku) and quickly play with it in 30seconds - Open authorizer instance endpoint in browser
<br/><br/> - Sign up as an admin with a secure password
[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku) - Configure environment variables from authorizer dashboard. Check env [docs](/core/env) for more information
# Install instance on railway > Note: `DATABASE_URL`, `DATABASE_TYPE` and `DATABASE_NAME` are only configurable via platform envs
Deploy production ready Authorizer instance using [railway.app](https://github.com/authorizerdev/authorizer-railway) with postgres and redis for free and build with it in 30seconds
<br/>
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2Fauthorizerdev%2Fauthorizer-railway&plugins=postgresql%2Credis&envs=ENV%2CDATABASE_TYPE%2CADMIN_SECRET%2CCOOKIE_NAME%2CJWT_ROLE_CLAIM%2CJWT_TYPE%2CJWT_SECRET%2CFACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&optionalEnvs=FACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&ENVDesc=Deployment+environment&DATABASE_TYPEDesc=With+railway+we+are+deploying+postgres+db&ADMIN_SECRETDesc=Secret+to+access+the+admin+apis&COOKIE_NAMEDesc=Name+of+http+only+cookie+that+will+be+used+as+session&FACEBOOK_CLIENT_IDDesc=Facebook+client+ID+for+facebook+login&FACEBOOK_CLIENT_SECRETDesc=Facebook+client+secret+for+facebook+login&GOOGLE_CLIENT_IDDesc=Google+client+ID+for+google+login&GOOGLE_CLIENT_SECRETDesc=Google+client+secret+for+google+login&GITHUB_CLIENT_IDDesc=Github+client+ID+for+github+login&GITHUB_CLIENT_SECRETDesc=Github+client+secret+for+github+login&ALLOWED_ORIGINSDesc=Whitelist+the+URL+for+which+this+instance+of+authorizer+is+allowed&ROLESDesc=Comma+separated+list+of+roles+that+platform+supports.+Default+role+is+user&PROTECTED_ROLESDesc=Comma+separated+list+of+protected+roles+for+which+sign-up+is+disabled&DEFAULT_ROLESDesc=Default+role+that+should+be+assigned+to+user.+It+should+be+one+from+the+list+of+%60ROLES%60+env.+Default+role+is+user&JWT_ROLE_CLAIMDesc=JWT+key+to+be+used+to+validate+the+role+field.&JWT_TYPEDesc=JWT+encryption+type&JWT_SECRETDesc=Random+string+that+will+be+used+for+encrypting+the+JWT+token&ENVDefault=PRODUCTION&DATABASE_TYPEDefault=postgres&COOKIE_NAMEDefault=authorizer&JWT_TYPEDefault=HS256&JWT_ROLE_CLAIMDefault=role)
### Things to consider ### Things to consider
- For social logins, you will need respective social platform key and secret - For social logins, you will need respective social platform key and secret
- For having verified users, you will need an SMTP server with an email address and password using which system can send emails. The system will send a verification link to an email address. Once an email is verified then, only able to access it. - For having verified users, you will need an SMTP server with an email address and password using which system can send emails. The system will send a verification link to an email address. Once an email is verified then, only able to access it.
> Note: One can always disable the email verification to allow open sign up, which is not recommended for production as anyone can use anyone's email address 😅 > Note: One can always disable the email verification to allow open sign up, which is not recommended for production as anyone can use anyone's email address 😅
- For persisting user sessions, you will need Redis URL (not in case of railway.app). If you do not configure a Redis server, sessions will be persisted until the instance is up or not restarted. For better response time on authorization requests/middleware, we recommend deploying Redis on the same infra/network as your authorizer server. - For persisting user sessions, you will need Redis URL (not in case of railway app). If you do not configure a Redis server, sessions will be persisted until the instance is up or not restarted. For better response time on authorization requests/middleware, we recommend deploying Redis on the same infra/network as your authorizer server.
## Testing ## Testing
@@ -166,8 +166,9 @@ This example demonstrates how you can use [`@authorizerdev/authorizer-js`](/auth
<script type="text/javascript"> <script type="text/javascript">
const authorizerRef = new authorizerdev.Authorizer({ const authorizerRef = new authorizerdev.Authorizer({
authorizerURL: `AUTHORIZER_URL`, authorizerURL: `YOUR_AUTHORIZER_INSTANCE_URL`,
redirectURL: window.location.origin, redirectURL: window.location.origin,
clientID: 'YOUR_CLIENT_ID', // obtain your client id from authorizer dashboard
}); });
// use the button selector as per your application // use the button selector as per your application
@@ -178,15 +179,19 @@ This example demonstrates how you can use [`@authorizerdev/authorizer-js`](/auth
}); });
async function onLoad() { async function onLoad() {
const res = await authorizerRef.browserLogin(); const res = await authorizerRef.authorize({
if (res && res.user) { response_type: 'code',
use_refresh_token: false,
});
if (res && res.access_token) {
// you can use user information here, eg: // you can use user information here, eg:
/** const user = await authorizerRef.getProfile({
const userSection = document.getElementById('user'); Authorization: `Bearer ${res.access_token}`,
const logoutSection = document.getElementById('logout-section'); });
logoutSection.classList.toggle('hide'); const userSection = document.getElementById('user');
userSection.innerHTML = `Welcome, ${res.user.email}`; const logoutSection = document.getElementById('logout-section');
*/ logoutSection.classList.toggle('hide');
userSection.innerHTML = `Welcome, ${user.email}`;
} }
} }
onLoad(); onLoad();

11
TODO.md
View File

@@ -1,5 +1,16 @@
# Task List # Task List
## Implement better way of handling jwt tokens
Check: https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/#server-side-rendering-ssr
- [x] Set finger print in response cookie (https://github.com/hasura/jwt-guide/blob/60a7a86146d604fc48a799fffdee712be1c52cd0/lib/setFingerprintCookieAndSignJwt.ts#L8)
- [x] Save refresh token in session store
- [x] refresh token should be made more secure with the help of secure token rotation. Every time new token is requested new refresh token should be generated
- [x] Return jwt in response
- [x] To get session send finger print and refresh token [if they are valid -> a new access token is generated and sent to user]
- [x] Refresh token should be long living token (refresh token + finger print hash should be verified)
## Open ID compatible claims and schema ## Open ID compatible claims and schema
- [x] Rename `schema.graphqls` and re generate schema - [x] Rename `schema.graphqls` and re generate schema

6
app/.prettierrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"useTabs": true
}

View File

@@ -1,3 +1,14 @@
# Authorizer APP # Authorizer APP
App that can be used as login wall for your any application in combination with @authorizerdev/@authorizer.js App that can be used as login wall for your any application in combination with @authorizerdev/@authorizer.js
### Getting started
**Setting up locally**
- `cd app`
- `npm start`
**Creating production build**
- `make build-app`

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
app/favicon_io/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

767
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,13 +5,14 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js", "build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
"start": "NODE_ENV=development node ./esbuild.config.js" "start": "NODE_ENV=development node ./esbuild.config.js",
"format": "prettier --write 'src/**/*.(ts|tsx|js|jsx)'"
}, },
"keywords": [], "keywords": [],
"author": "Lakhan Samani", "author": "Lakhan Samani",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "latest", "@authorizerdev/authorizer-react": "^1.3.2",
"@types/react": "^17.0.15", "@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17", "esbuild": "^0.12.17",
@@ -19,9 +20,12 @@
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-is": "^17.0.2", "react-is": "^17.0.2",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"styled-components": "^5.3.0",
"typescript": "^4.3.5" "typescript": "^4.3.5"
}, },
"devDependencies": { "devDependencies": {
"@types/react-router-dom": "^5.1.8" "@types/react-router-dom": "^5.1.8",
"@types/styled-components": "^5.1.11",
"prettier": "2.7.1"
} }
} }

626
app/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,626 @@
lockfileVersion: 5.4
specifiers:
'@authorizerdev/authorizer-react': ^1.1.9
'@types/react': ^17.0.15
'@types/react-dom': ^17.0.9
'@types/react-router-dom': ^5.1.8
'@types/styled-components': ^5.1.11
esbuild: ^0.12.17
prettier: 2.7.1
react: ^17.0.2
react-dom: ^17.0.2
react-is: ^17.0.2
react-router-dom: ^5.2.0
styled-components: ^5.3.0
typescript: ^4.3.5
dependencies:
'@authorizerdev/authorizer-react': 1.1.9_react@17.0.2
'@types/react': 17.0.53
'@types/react-dom': 17.0.19
esbuild: 0.12.29
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
react-is: 17.0.2
react-router-dom: 5.3.4_react@17.0.2
styled-components: 5.3.9_fane7jikarojcev26y27hpbhu4
typescript: 4.9.5
devDependencies:
'@types/react-router-dom': 5.3.3
'@types/styled-components': 5.1.26
prettier: 2.7.1
packages:
/@authorizerdev/authorizer-js/1.2.1:
resolution: {integrity: sha512-/nFARvsHyZUsGFKrcYi8hgpnbThYR/NMJ2BJdQpWy/x7QsBnfLeCChBYWncbYHSIjFCa5PPKKfvhXM56HqVqsw==}
engines: {node: '>=10'}
dependencies:
cross-fetch: 3.1.5
transitivePeerDependencies:
- encoding
dev: false
/@authorizerdev/authorizer-react/1.1.9_react@17.0.2:
resolution: {integrity: sha512-BlB4ixEm9nf+yjZ9OqIWbx5fMTmzeByEsNDAd5iYkt6HB+3Sk53DGiO5h6SgJznzPyqAwl8yg6y/QgbZreDTFA==}
engines: {node: '>=10'}
peerDependencies:
react: '>=16'
dependencies:
'@authorizerdev/authorizer-js': 1.2.1
react: 17.0.2
transitivePeerDependencies:
- encoding
dev: false
/@babel/code-frame/7.18.6:
resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/highlight': 7.18.6
dev: false
/@babel/generator/7.21.3:
resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
'@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.17
jsesc: 2.5.2
dev: false
/@babel/helper-annotate-as-pure/7.18.6:
resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-environment-visitor/7.18.9:
resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-function-name/7.21.0:
resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
'@babel/types': 7.21.3
dev: false
/@babel/helper-hoist-variables/7.18.6:
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-module-imports/7.18.6:
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-split-export-declaration/7.18.6:
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-string-parser/7.19.4:
resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-validator-identifier/7.19.1:
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/highlight/7.18.6:
resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.19.1
chalk: 2.4.2
js-tokens: 4.0.0
dev: false
/@babel/parser/7.21.3:
resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/runtime/7.21.0:
resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
dev: false
/@babel/template/7.20.7:
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/parser': 7.21.3
'@babel/types': 7.21.3
dev: false
/@babel/traverse/7.21.3_supports-color@5.5.0:
resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.21.3
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.21.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.21.3
'@babel/types': 7.21.3
debug: 4.3.4_supports-color@5.5.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: false
/@babel/types/7.21.3:
resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.19.4
'@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0
dev: false
/@emotion/is-prop-valid/1.2.0:
resolution: {integrity: sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==}
dependencies:
'@emotion/memoize': 0.8.0
dev: false
/@emotion/memoize/0.8.0:
resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==}
dev: false
/@emotion/stylis/0.8.5:
resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==}
dev: false
/@emotion/unitless/0.7.5:
resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
dev: false
/@jridgewell/gen-mapping/0.3.2:
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
'@jridgewell/trace-mapping': 0.3.17
dev: false
/@jridgewell/resolve-uri/3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
dev: false
/@jridgewell/set-array/1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
engines: {node: '>=6.0.0'}
dev: false
/@jridgewell/sourcemap-codec/1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: false
/@jridgewell/trace-mapping/0.3.17:
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: false
/@types/history/4.7.11:
resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==}
dev: true
/@types/hoist-non-react-statics/3.3.1:
resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==}
dependencies:
'@types/react': 17.0.53
hoist-non-react-statics: 3.3.2
dev: true
/@types/prop-types/15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
/@types/react-dom/17.0.19:
resolution: {integrity: sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ==}
dependencies:
'@types/react': 17.0.53
dev: false
/@types/react-router-dom/5.3.3:
resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
dependencies:
'@types/history': 4.7.11
'@types/react': 17.0.53
'@types/react-router': 5.1.20
dev: true
/@types/react-router/5.1.20:
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
dependencies:
'@types/history': 4.7.11
'@types/react': 17.0.53
dev: true
/@types/react/17.0.53:
resolution: {integrity: sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.3
csstype: 3.1.1
/@types/scheduler/0.16.3:
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
/@types/styled-components/5.1.26:
resolution: {integrity: sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==}
dependencies:
'@types/hoist-non-react-statics': 3.3.1
'@types/react': 17.0.53
csstype: 3.1.1
dev: true
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
dependencies:
color-convert: 1.9.3
dev: false
/babel-plugin-styled-components/2.0.7_styled-components@5.3.9:
resolution: {integrity: sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==}
peerDependencies:
styled-components: '>= 2'
dependencies:
'@babel/helper-annotate-as-pure': 7.18.6
'@babel/helper-module-imports': 7.18.6
babel-plugin-syntax-jsx: 6.18.0
lodash: 4.17.21
picomatch: 2.3.1
styled-components: 5.3.9_fane7jikarojcev26y27hpbhu4
dev: false
/babel-plugin-syntax-jsx/6.18.0:
resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==}
dev: false
/camelize/1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
dev: false
/chalk/2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
dependencies:
ansi-styles: 3.2.1
escape-string-regexp: 1.0.5
supports-color: 5.5.0
dev: false
/color-convert/1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
color-name: 1.1.3
dev: false
/color-name/1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
dev: false
/cross-fetch/3.1.5:
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
dependencies:
node-fetch: 2.6.7
transitivePeerDependencies:
- encoding
dev: false
/css-color-keywords/1.0.0:
resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
engines: {node: '>=4'}
dev: false
/css-to-react-native/3.2.0:
resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
dependencies:
camelize: 1.0.1
css-color-keywords: 1.0.0
postcss-value-parser: 4.2.0
dev: false
/csstype/3.1.1:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
/debug/4.3.4_supports-color@5.5.0:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
supports-color: 5.5.0
dev: false
/esbuild/0.12.29:
resolution: {integrity: sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==}
hasBin: true
requiresBuild: true
dev: false
/escape-string-regexp/1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
dev: false
/globals/11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
dev: false
/has-flag/3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
dev: false
/history/4.10.1:
resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
dependencies:
'@babel/runtime': 7.21.0
loose-envify: 1.4.0
resolve-pathname: 3.0.0
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
value-equal: 1.0.1
dev: false
/hoist-non-react-statics/3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
dependencies:
react-is: 16.13.1
/isarray/0.0.1:
resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==}
dev: false
/js-tokens/4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: false
/jsesc/2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
hasBin: true
dev: false
/lodash/4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: false
/loose-envify/1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
dependencies:
js-tokens: 4.0.0
dev: false
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: false
/node-fetch/2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: false
/object-assign/4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
dev: false
/path-to-regexp/1.8.0:
resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==}
dependencies:
isarray: 0.0.1
dev: false
/picomatch/2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: false
/postcss-value-parser/4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
dev: false
/prettier/2.7.1:
resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
/prop-types/15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
dev: false
/react-dom/17.0.2_react@17.0.2:
resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==}
peerDependencies:
react: 17.0.2
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react: 17.0.2
scheduler: 0.20.2
dev: false
/react-is/16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
/react-is/17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
dev: false
/react-router-dom/5.3.4_react@17.0.2:
resolution: {integrity: sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==}
peerDependencies:
react: '>=15'
dependencies:
'@babel/runtime': 7.21.0
history: 4.10.1
loose-envify: 1.4.0
prop-types: 15.8.1
react: 17.0.2
react-router: 5.3.4_react@17.0.2
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
dev: false
/react-router/5.3.4_react@17.0.2:
resolution: {integrity: sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==}
peerDependencies:
react: '>=15'
dependencies:
'@babel/runtime': 7.21.0
history: 4.10.1
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0
path-to-regexp: 1.8.0
prop-types: 15.8.1
react: 17.0.2
react-is: 16.13.1
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
dev: false
/react/17.0.2:
resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==}
engines: {node: '>=0.10.0'}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/regenerator-runtime/0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
dev: false
/resolve-pathname/3.0.0:
resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==}
dev: false
/scheduler/0.20.2:
resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/shallowequal/1.1.0:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
dev: false
/styled-components/5.3.9_fane7jikarojcev26y27hpbhu4:
resolution: {integrity: sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A==}
engines: {node: '>=10'}
peerDependencies:
react: '>= 16.8.0'
react-dom: '>= 16.8.0'
react-is: '>= 16.8.0'
dependencies:
'@babel/helper-module-imports': 7.18.6
'@babel/traverse': 7.21.3_supports-color@5.5.0
'@emotion/is-prop-valid': 1.2.0
'@emotion/stylis': 0.8.5
'@emotion/unitless': 0.7.5
babel-plugin-styled-components: 2.0.7_styled-components@5.3.9
css-to-react-native: 3.2.0
hoist-non-react-statics: 3.3.2
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
react-is: 17.0.2
shallowequal: 1.1.0
supports-color: 5.5.0
dev: false
/supports-color/5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: false
/tiny-invariant/1.3.1:
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
dev: false
/tiny-warning/1.0.3:
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
dev: false
/to-fast-properties/2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
dev: false
/tr46/0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: false
/typescript/4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: false
/value-equal/1.0.1:
resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==}
dev: false
/webidl-conversions/3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: false
/whatwg-url/5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: false

View File

@@ -2,10 +2,37 @@ import React from 'react';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
import { AuthorizerProvider } from '@authorizerdev/authorizer-react'; import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
import Root from './Root'; import Root from './Root';
import { createRandomString } from './utils/common';
declare global {
interface Window {
__authorizer__: any;
}
}
export default function App() { export default function App() {
// @ts-ignore const searchParams = new URLSearchParams(window.location.search);
const globalState: Record<string, string> = window['__authorizer__']; const state = searchParams.get('state') || createRandomString();
const scope = searchParams.get('scope')
? searchParams.get('scope')?.toString().split(' ')
: `openid profile email`;
const urlProps: Record<string, any> = {
state,
scope,
};
const redirectURL =
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
if (redirectURL) {
urlProps.redirectURL = redirectURL;
} else {
urlProps.redirectURL = window.location.href;
}
const globalState: Record<string, string> = {
...window['__authorizer__'],
...urlProps,
};
return ( return (
<div <div
style={{ style={{
@@ -26,27 +53,19 @@ export default function App() {
<img <img
src={`${globalState.organizationLogo}`} src={`${globalState.organizationLogo}`}
alt="logo" alt="logo"
style={{ height: 60, width: 60, objectFit: 'cover' }} style={{ height: 60, objectFit: 'cover' }}
/> />
<h1>{globalState.organizationName}</h1> <h1>{globalState.organizationName}</h1>
</div> </div>
<div <div className="container">
style={{
width: 400,
margin: `10px auto`,
border: `1px solid #D1D5DB`,
padding: `25px 20px`,
borderRadius: 5,
}}
>
<BrowserRouter> <BrowserRouter>
<AuthorizerProvider <AuthorizerProvider
config={{ config={{
authorizerURL: globalState.authorizerURL, authorizerURL: window.location.origin,
redirectURL: globalState.redirectURL, redirectURL: globalState.redirectURL,
}} }}
> >
<Root /> <Root globalState={globalState} />
</AuthorizerProvider> </AuthorizerProvider>
</BrowserRouter> </BrowserRouter>
</div> </div>

View File

@@ -1,23 +1,94 @@
import React, { useEffect, lazy, Suspense } from 'react'; import React, { useEffect, lazy, Suspense } from 'react';
import { Switch, Route } from 'react-router-dom'; import { Switch, Route } from 'react-router-dom';
import { useAuthorizer } from '@authorizerdev/authorizer-react'; import { useAuthorizer } from '@authorizerdev/authorizer-react';
import styled, { ThemeProvider } from 'styled-components';
import SetupPassword from './pages/setup-password';
import { hasWindow, createRandomString } from './utils/common';
import { theme } from './theme';
const ResetPassword = lazy(() => import('./pages/rest-password')); const ResetPassword = lazy(() => import('./pages/rest-password'));
const Login = lazy(() => import('./pages/login')); const Login = lazy(() => import('./pages/login'));
const Dashboard = lazy(() => import('./pages/dashboard')); const Dashboard = lazy(() => import('./pages/dashboard'));
const SignUp = lazy(() => import('./pages/signup'));
export default function Root() { const Wrapper = styled.div`
font-family: ${(props) => props.theme.fonts.fontStack};
color: ${(props) => props.theme.colors.textColor};
font-size: ${(props) => props.theme.fonts.mediumText};
box-sizing: border-box;
*,
*:before,
*:after {
box-sizing: inherit;
}
`;
export default function Root({
globalState,
}: {
globalState: Record<string, string>;
}) {
const { token, loading, config } = useAuthorizer(); const { token, loading, config } = useAuthorizer();
const searchParams = new URLSearchParams(
hasWindow() ? window.location.search : ``,
);
const state = searchParams.get('state') || createRandomString();
const scope = searchParams.get('scope')
? searchParams.get('scope')?.toString().split(' ')
: ['openid', 'profile', 'email'];
const code = searchParams.get('code') || '';
const nonce = searchParams.get('nonce') || '';
const urlProps: Record<string, any> = {
state,
scope,
};
const redirectURL =
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
if (redirectURL) {
urlProps.redirectURL = redirectURL;
} else {
urlProps.redirectURL = hasWindow() ? window.location.origin : redirectURL;
}
urlProps.redirect_uri = urlProps.redirectURL;
useEffect(() => { useEffect(() => {
if (token) { if (token) {
const url = new URL(config.redirectURL || '/app'); let redirectURL = config.redirectURL || '/app';
// let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
// Note: If OIDC breaks in the future, use the above params
let params = `state=${globalState.state}`;
if (code !== '') {
params += `&code=${code}`;
}
if (nonce !== '') {
params += `&nonce=${nonce}`;
}
if (token.refresh_token) {
params += `&refresh_token=${token.refresh_token}`;
}
const url = new URL(redirectURL);
if (redirectURL.includes('?')) {
redirectURL = `${redirectURL}&${params}`;
} else {
redirectURL = `${redirectURL}?${params}`;
}
if (url.origin !== window.location.origin) { if (url.origin !== window.location.origin) {
window.location.href = config.redirectURL || '/app'; sessionStorage.removeItem('authorizer_state');
window.location.replace(redirectURL);
} }
} }
return () => {}; return () => {};
}, [token]); }, [token, config]);
if (loading) { if (loading) {
return <h1>Loading...</h1>; return <h1>Loading...</h1>;
@@ -37,14 +108,24 @@ export default function Root() {
return ( return (
<Suspense fallback={<></>}> <Suspense fallback={<></>}>
<Switch> <ThemeProvider theme={theme}>
<Route path="/app" exact> <Wrapper>
<Login /> <Switch>
</Route> <Route path="/app" exact>
<Route path="/app/reset-password"> <Login urlProps={urlProps} />
<ResetPassword /> </Route>
</Route> <Route path="/app/signup">
</Switch> <SignUp urlProps={urlProps} />
</Route>
<Route path="/app/reset-password">
<ResetPassword />
</Route>
<Route path="/app/setup-password">
<SetupPassword />
</Route>
</Switch>
</Wrapper>
</ThemeProvider>
</Suspense> </Suspense>
); );
} }

View File

@@ -1,5 +1,5 @@
body { body {
margin: 0; margin: 10;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif; sans-serif;
@@ -14,3 +14,17 @@ body {
*:after { *:after {
box-sizing: inherit; box-sizing: inherit;
} }
.container {
box-sizing: content-box;
border: 1px solid #d1d5db;
padding: 25px 20px;
border-radius: 5px;
}
@media only screen and (min-width: 768px) {
.container {
width: 400px;
margin: 0 auto;
}
}

View File

@@ -15,7 +15,7 @@ export default function Dashboard() {
return ( return (
<div> <div>
<h1>Hey 👋,</h1> <h1>Hey 👋,</h1>
<p>Thank you for joining authorizer demo app.</p> <p>Thank you for using authorizer.</p>
<p> <p>
Your email address is{' '} Your email address is{' '}
<a href={`mailto:${user?.email}`} style={{ color: '#3B82F6' }}> <a href={`mailto:${user?.email}`} style={{ color: '#3B82F6' }}>

View File

@@ -1,10 +1,98 @@
import React, { Fragment } from 'react'; import React, { Fragment, useState } from 'react';
import { Authorizer } from '@authorizerdev/authorizer-react'; import {
AuthorizerBasicAuthLogin,
AuthorizerForgotPassword,
AuthorizerMagicLinkLogin,
AuthorizerSocialLogin,
useAuthorizer,
} from '@authorizerdev/authorizer-react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
export default function Login() { const enum VIEW_TYPES {
LOGIN = 'login',
FORGOT_PASSWORD = 'forgot-password',
}
const Footer = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 15px;
`;
const FooterContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
`;
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
const { config } = useAuthorizer();
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
const isBasicAuth = config.is_basic_authentication_enabled;
return ( return (
<Fragment> <Fragment>
<Authorizer /> {view === VIEW_TYPES.LOGIN && (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Login</h1>
<AuthorizerSocialLogin urlProps={urlProps} />
<br />
{(config.is_basic_authentication_enabled ||
config.is_mobile_basic_authentication_enabled) &&
!config.is_magic_link_login_enabled && (
<AuthorizerBasicAuthLogin urlProps={urlProps} />
)}
{config.is_magic_link_login_enabled && (
<AuthorizerMagicLinkLogin urlProps={urlProps} />
)}
{(config.is_basic_authentication_enabled ||
config.is_mobile_basic_authentication_enabled) &&
!config.is_magic_link_login_enabled && (
<Footer>
<Link
to="#"
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
style={{ marginBottom: 10 }}
>
Forgot Password?
</Link>
</Footer>
)}
</Fragment>
)}
{view === VIEW_TYPES.FORGOT_PASSWORD && (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
<AuthorizerForgotPassword
urlProps={{
...urlProps,
redirect_uri: `${window.location.origin}/app/reset-password`,
}}
onPasswordReset={() => {
setView(VIEW_TYPES.LOGIN);
}}
/>
<Footer>
<Link
to="#"
onClick={() => setView(VIEW_TYPES.LOGIN)}
style={{ marginBottom: 10 }}
>
Back
</Link>
</Footer>
</Fragment>
)}
{config.is_basic_authentication_enabled &&
!config.is_magic_link_login_enabled &&
config.is_sign_up_enabled && (
<FooterContent>
Don't have an account? &nbsp; <Link to="/app/signup"> Sign Up</Link>
</FooterContent>
)}
</Fragment> </Fragment>
); );
} }

View File

@@ -0,0 +1,12 @@
import React, { Fragment } from 'react';
import { AuthorizerResetPassword } from '@authorizerdev/authorizer-react';
export default function SetupPassword() {
return (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Setup new Password</h1>
<br />
<AuthorizerResetPassword />
</Fragment>
);
}

29
app/src/pages/signup.tsx Normal file
View File

@@ -0,0 +1,29 @@
import React, { Fragment } from 'react';
import { AuthorizerSignup, AuthorizerSocialLogin } from '@authorizerdev/authorizer-react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
const FooterContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
`;
export default function SignUp({
urlProps,
}: {
urlProps: Record<string, any>;
}) {
return (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
<br />
<AuthorizerSocialLogin urlProps={urlProps} />
<AuthorizerSignup urlProps={urlProps} />
<FooterContent>
Already have an account? <Link to="/app"> Login</Link>
</FooterContent>
</Fragment>
);
}

28
app/src/theme.ts Normal file
View File

@@ -0,0 +1,28 @@
// colors: https://tailwindcss.com/docs/customizing-colors
export const theme = {
colors: {
primary: '#3B82F6',
primaryDisabled: '#60A5FA',
gray: '#D1D5DB',
danger: '#DC2626',
success: '#10B981',
textColor: '#374151',
},
fonts: {
// typography
fontStack: '-apple-system, system-ui, sans-serif',
// font sizes
largeText: '18px',
mediumText: '14px',
smallText: '12px',
tinyText: '10px',
},
radius: {
card: '5px',
button: '5px',
input: '5px',
},
};

24
app/src/utils/common.ts Normal file
View File

@@ -0,0 +1,24 @@
export const getCrypto = () => {
//ie 11.x uses msCrypto
return (window.crypto || (window as any).msCrypto) as Crypto;
};
export const createRandomString = () => {
const charset =
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
let random = '';
const randomValues = Array.from(
getCrypto().getRandomValues(new Uint8Array(43)),
);
randomValues.forEach((v) => (random += charset[v % charset.length]));
return random;
};
export const createQueryParams = (params: any) => {
return Object.keys(params)
.filter((k) => typeof params[k] !== 'undefined')
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
.join('&');
};
export const hasWindow = (): boolean => typeof window !== 'undefined';

619
app/yarn.lock Normal file
View File

@@ -0,0 +1,619 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@authorizerdev/authorizer-js@^2.0.3":
version "2.0.3"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-2.0.3.tgz"
integrity sha512-uencwr3Ea8mwfxVKDFf2ITRCRSmzvua+O2voRuiWQORtRQTgZQjkN3M+IEkEj+WP9M1iFIl+NDgzECsp8ptC/A==
dependencies:
cross-fetch "^3.1.5"
"@authorizerdev/authorizer-react@^1.3.2":
version "1.3.2"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.3.2.tgz"
integrity sha512-3kMAygHBCa8Fc9Oo0lz1k88r+Pd6kx1PSn3NMYLwxQXy2jRt4xWn7iuGn+SDGFs3DzofaN71I61gRwQ+6dO1rw==
dependencies:
"@authorizerdev/authorizer-js" "^2.0.3"
validator "^13.11.0"
"@babel/code-frame@^7.22.13":
version "7.22.13"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz"
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
dependencies:
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/generator@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz"
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
dependencies:
"@babel/types" "^7.23.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.16.0":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz"
integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-environment-visitor@^7.22.20":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-function-name@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies:
"@babel/template" "^7.22.15"
"@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.22.5":
version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz"
integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-split-export-declaration@^7.22.6":
version "7.22.6"
resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-string-parser@^7.22.5":
version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
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:
"@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1":
version "7.14.8"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz"
integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.22.15":
version "7.22.15"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/traverse@^7.4.5":
version "7.23.2"
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz"
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.0"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.23.0"
"@babel/types" "^7.23.0"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.16.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz"
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
dependencies:
"@emotion/memoize" "0.7.4"
"@emotion/memoize@0.7.4":
version "0.7.4"
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz"
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
"@emotion/stylis@^0.8.4":
version "0.8.5"
resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz"
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
"@emotion/unitless@^0.7.4":
version "0.7.5"
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
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@*":
version "4.7.9"
resolved "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz"
integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
"@types/hoist-non-react-statics@*":
version "3.3.1"
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/prop-types@*":
version "15.7.4"
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz"
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
"@types/react-dom@^17.0.9":
version "17.0.9"
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz"
integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==
dependencies:
"@types/react" "*"
"@types/react-router-dom@^5.1.8":
version "5.1.8"
resolved "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz"
integrity sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*":
version "5.1.16"
resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz"
integrity sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.15":
version "17.0.15"
resolved "https://registry.npmjs.org/@types/react/-/react-17.0.15.tgz"
integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/scheduler@*":
version "0.16.2"
resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
"@types/styled-components@^5.1.11":
version "5.1.25"
resolved "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz"
integrity sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==
dependencies:
"@types/hoist-non-react-statics" "*"
"@types/react" "*"
csstype "^3.0.2"
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
"babel-plugin-styled-components@>= 1.12.0":
version "2.0.2"
resolved "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz"
integrity sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.16.0"
"@babel/helper-module-imports" "^7.16.0"
babel-plugin-syntax-jsx "^6.18.0"
lodash "^4.17.11"
babel-plugin-syntax-jsx@^6.18.0:
version "6.18.0"
resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz"
integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
camelize@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
cross-fetch@^3.1.5:
version "3.1.8"
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz"
integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==
dependencies:
node-fetch "^2.6.12"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz"
integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
css-to-react-native@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz"
integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==
dependencies:
camelize "^1.0.0"
css-color-keywords "^1.0.0"
postcss-value-parser "^4.0.2"
csstype@^3.0.2:
version "3.0.8"
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz"
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
debug@^4.1.0:
version "4.3.3"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
esbuild@^0.12.17:
version "0.12.17"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz"
integrity sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
history@^4.9.0:
version "4.10.1"
resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz"
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
dependencies:
"@babel/runtime" "^7.1.2"
loose-envify "^1.2.0"
resolve-pathname "^3.0.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
version "3.3.2"
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
dependencies:
react-is "^16.7.0"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
lodash@^4.17.11:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
mini-create-react-context@^0.4.0:
version "0.4.1"
resolved "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz"
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
dependencies:
"@babel/runtime" "^7.12.1"
tiny-warning "^1.0.3"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-fetch@^2.6.12:
version "2.7.0"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
postcss-value-parser@^4.0.2:
version "4.2.0"
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
prettier@2.7.1:
version "2.7.1"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz"
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
prop-types@^15.0.0, prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"
react-dom@^17.0.2, "react-dom@>= 16.8.0":
version "17.0.2"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "^0.20.2"
react-is@^16.6.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^17.0.2, "react-is@>= 16.8.0":
version "17.0.2"
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-router-dom@^5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz"
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.2.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz"
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1"
mini-create-react-context "^0.4.0"
path-to-regexp "^1.7.0"
prop-types "^15.6.2"
react-is "^16.6.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
"react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", react@^17.0.2, "react@>= 16.8.0", react@>=15, react@>=16, react@17.0.2:
version "17.0.2"
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
styled-components@^5.3.0, "styled-components@>= 2":
version "5.3.3"
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz"
integrity sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/traverse" "^7.4.5"
"@emotion/is-prop-valid" "^0.8.8"
"@emotion/stylis" "^0.8.4"
"@emotion/unitless" "^0.7.4"
babel-plugin-styled-components ">= 1.12.0"
css-to-react-native "^3.0.0"
hoist-non-react-statics "^3.0.0"
shallowequal "^1.1.0"
supports-color "^5.5.0"
supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
tiny-invariant@^1.0.2:
version "1.1.0"
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz"
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
typescript@^4.3.5:
version "4.3.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz"
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
validator@^13.11.0:
version "13.11.0"
resolved "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz"
integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==
value-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz"
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"

View File

@@ -0,0 +1,6 @@
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"useTabs": true
}

12
dashboard/README.md Normal file
View File

@@ -0,0 +1,12 @@
# Authorizer dashboard
### Getting started
**Setting up locally**
- `cd dashboard`
- `npm start`
**Creating production build**
- `make build-dashboard`

View File

@@ -0,0 +1,12 @@
const __is_prod__ = process.env.NODE_ENV === 'production';
require('esbuild').build({
entryPoints: ['src/index.tsx'],
chunkNames: '[name]-[hash]',
bundle: true,
minify: __is_prod__,
outdir: 'build',
splitting: true,
format: 'esm',
watch: !__is_prod__,
logLevel: 'info',
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

3092
dashboard/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
dashboard/package.json Normal file
View File

@@ -0,0 +1,42 @@
{
"name": "dashboard",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
"start": "NODE_ENV=development node ./esbuild.config.js",
"format": "prettier --write --use-tabs 'src/**/*.(ts|tsx|js|jsx)'"
},
"keywords": [],
"author": "Lakhan Samani",
"license": "ISC",
"dependencies": {
"@chakra-ui/react": "^1.7.3",
"@emotion/core": "^11.0.0",
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.2",
"dayjs": "^1.10.7",
"esbuild": "^0.14.9",
"focus-visible": "^5.2.0",
"framer-motion": "^5.5.5",
"graphql": "^16.2.0",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-draft-wysiwyg": "^1.15.0",
"react-dropzone": "^12.0.4",
"react-email-editor": "^1.6.1",
"react-icons": "^4.3.1",
"react-router-dom": "^6.2.1",
"typescript": "^4.5.4",
"urql": "^2.0.6"
},
"devDependencies": {
"@types/react-email-editor": "^1.1.7",
"prettier": "2.7.1"
}
}

BIN
dashboard/public/roblox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
foo@bar.com,test@authorizer.dev
1 foo bar.com,test authorizer.dev

52
dashboard/src/App.tsx Normal file
View File

@@ -0,0 +1,52 @@
import * as React from 'react';
import { Fragment } from 'react';
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
import { BrowserRouter } from 'react-router-dom';
import { createClient, Provider } from 'urql';
import { AppRoutes } from './routes';
import { AuthContextProvider } from './contexts/AuthContext';
const queryClient = createClient({
url: '/graphql',
fetchOptions: () => {
return {
credentials: 'include',
headers: {
'x-authorizer-url': window.location.origin,
},
};
},
requestPolicy: 'network-only',
});
const theme = extendTheme({
styles: {
global: {
'html, body, #root': {
height: '100%',
outline: 'none',
},
},
},
colors: {
blue: {
500: 'rgb(59,130,246)',
},
},
});
export default function App() {
return (
<Fragment>
<ChakraProvider theme={theme}>
<Provider value={queryClient}>
<BrowserRouter basename="/dashboard">
<AuthContextProvider>
<AppRoutes />
</AuthContextProvider>
</BrowserRouter>
</Provider>
</ChakraProvider>
</Fragment>
);
}

View File

@@ -0,0 +1,106 @@
import React from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaRegTrashAlt } from 'react-icons/fa';
import { DeleteEmailTemplate } from '../graphql/mutation';
import { capitalizeFirstLetter } from '../utils';
interface deleteEmailTemplateModalInputPropTypes {
emailTemplateId: string;
eventName: string;
fetchEmailTemplatesData: Function;
}
const DeleteEmailTemplateModal = ({
emailTemplateId,
eventName,
fetchEmailTemplatesData,
}: deleteEmailTemplateModalInputPropTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const deleteHandler = async () => {
const res = await client
.mutation(DeleteEmailTemplate, { params: { id: emailTemplateId } })
.toPromise();
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
return;
} else if (res.data?._delete_email_template) {
toast({
title: capitalizeFirstLetter(res.data?._delete_email_template.message),
isClosable: true,
status: 'success',
position: 'top-right',
});
}
onClose();
fetchEmailTemplatesData();
};
return (
<>
<MenuItem onClick={onOpen}>Delete</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Delete Email Template</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text fontSize="md">Are you sure?</Text>
<Flex
padding="5%"
marginTop="5%"
marginBottom="2%"
border="1px solid #ff7875"
borderRadius="5px"
flexDirection="column"
>
<Text fontSize="sm">
Email template for event <b>{eventName}</b> will be deleted
permanently!
</Text>
</Flex>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaRegTrashAlt />}
colorScheme="red"
variant="solid"
onClick={deleteHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Delete
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default DeleteEmailTemplateModal;

View File

@@ -0,0 +1,112 @@
import React from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaRegTrashAlt } from 'react-icons/fa';
import { DeleteUser } from '../graphql/mutation';
import { capitalizeFirstLetter } from '../utils';
interface userDataTypes {
id: string;
email: string;
}
const DeleteUserModal = ({
user,
updateUserList,
}: {
user: userDataTypes;
updateUserList: Function;
}) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [userData, setUserData] = React.useState<userDataTypes>({
id: '',
email: '',
});
React.useEffect(() => {
setUserData(user);
}, []);
const deleteHandler = async () => {
const res = await client
.mutation(DeleteUser, { params: { email: userData.email } })
.toPromise();
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
return;
} else if (res.data?._delete_user) {
toast({
title: capitalizeFirstLetter(res.data?._delete_user.message),
isClosable: true,
status: 'success',
position: 'top-right',
});
}
onClose();
updateUserList();
};
return (
<>
<MenuItem onClick={onOpen}>Delete User</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Delete User</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text fontSize="md">Are you sure?</Text>
<Flex
padding="5%"
marginTop="5%"
marginBottom="2%"
border="1px solid #ff7875"
borderRadius="5px"
flexDirection="column"
>
<Text fontSize="sm">
User <b>{user.email}</b> will be deleted permanently!
</Text>
</Flex>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaRegTrashAlt />}
colorScheme="red"
variant="solid"
onClick={deleteHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Delete
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default DeleteUserModal;

View File

@@ -0,0 +1,106 @@
import React from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaRegTrashAlt } from 'react-icons/fa';
import { DeleteWebhook } from '../graphql/mutation';
import { capitalizeFirstLetter } from '../utils';
interface deleteWebhookModalInputPropTypes {
webhookId: string;
eventName: string;
fetchWebookData: Function;
}
const DeleteWebhookModal = ({
webhookId,
eventName,
fetchWebookData,
}: deleteWebhookModalInputPropTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const deleteHandler = async () => {
const res = await client
.mutation(DeleteWebhook, { params: { id: webhookId } })
.toPromise();
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
return;
} else if (res.data?._delete_webhook) {
toast({
title: capitalizeFirstLetter(res.data?._delete_webhook.message),
isClosable: true,
status: 'success',
position: 'top-right',
});
}
onClose();
fetchWebookData();
};
return (
<>
<MenuItem onClick={onOpen}>Delete</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Delete Webhook</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text fontSize="md">Are you sure?</Text>
<Flex
padding="5%"
marginTop="5%"
marginBottom="2%"
border="1px solid #ff7875"
borderRadius="5px"
flexDirection="column"
>
<Text fontSize="sm">
Webhook for event <b>{eventName}</b> will be deleted
permanently!
</Text>
</Flex>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaRegTrashAlt />}
colorScheme="red"
variant="solid"
onClick={deleteHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Delete
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default DeleteWebhookModal;

View File

@@ -0,0 +1,263 @@
import React, { useState } from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Stack,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa';
import InputField from './InputField';
import {
DateInputType,
MultiSelectInputType,
SelectInputType,
TextInputType,
} from '../constants';
import { getObjectDiff } from '../utils';
import { UpdateUser } from '../graphql/mutation';
import { GetAvailableRolesQuery } from '../graphql/queries';
const GenderTypes = {
Undisclosed: null,
Male: 'Male',
Female: 'Female',
};
interface userDataTypes {
id: string;
email: string;
given_name: string;
family_name: string;
middle_name: string;
nickname: string;
gender: string;
birthdate: string;
phone_number: string;
picture: string;
roles: [string] | [];
}
const EditUserModal = ({
user,
updateUserList,
}: {
user: userDataTypes;
updateUserList: Function;
}) => {
const client = useClient();
const toast = useToast();
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
const { isOpen, onOpen, onClose } = useDisclosure();
const [userData, setUserData] = useState<userDataTypes>({
id: '',
email: '',
given_name: '',
family_name: '',
middle_name: '',
nickname: '',
gender: '',
birthdate: '',
phone_number: '',
picture: '',
roles: [],
});
React.useEffect(() => {
setUserData(user);
fetchAvailableRoles();
}, []);
const fetchAvailableRoles = async () => {
const res = await client.query(GetAvailableRolesQuery).toPromise();
if (res.data?._env?.ROLES && res.data?._env?.PROTECTED_ROLES) {
setAvailableRoles([
...res.data._env.ROLES,
...res.data._env.PROTECTED_ROLES,
]);
}
};
const saveHandler = async () => {
const diff = getObjectDiff(user, userData);
const updatedUserData = diff.reduce(
(acc: any, property: string) => ({
...acc,
// @ts-ignore
[property]: userData[property],
}),
{},
);
const res = await client
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
.toPromise();
if (res.error) {
toast({
title: 'User data update failed',
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (res.data?._update_user?.id) {
toast({
title: 'User data update successful',
isClosable: true,
status: 'success',
position: 'top-right',
});
}
onClose();
updateUserList();
};
return (
<>
<MenuItem onClick={onOpen}>Edit User Details</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Edit User Details</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Stack>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Given Name:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.GIVEN_NAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Middle Name:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.MIDDLE_NAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Family Name:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.FAMILY_NAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Birth Date:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={DateInputType.BIRTHDATE}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Nickname:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.NICKNAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Gender:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={SelectInputType.GENDER}
value={userData.gender}
options={GenderTypes}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Phone Number:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.PHONE_NUMBER}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Picture:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.PICTURE}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Roles:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
availableRoles={availableRoles}
inputType={MultiSelectInputType.USER_ROLES}
/>
</Center>
</Flex>
</Stack>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaSave />}
colorScheme="blue"
variant="solid"
onClick={saveHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Save
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default EditUserModal;

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { Flex, Stack, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../../components/InputField';
import { TextInputType, TextAreaInputType } from '../../constants';
const AccessToken = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Access Token
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '50%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Access Token Expiry Time:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
placeholder="0h15m0s"
/>
</Flex>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '60%'}
justifyContent="start"
direction="column"
>
<Text fontSize="sm">Custom Scripts:</Text>
<Text fontSize="xs" color="blackAlpha.500">
(Used to add custom fields in ID token)
</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
placeholder="Add script here"
minH="25vh"
/>
</Flex>
</Flex>
</Stack>
</div>
);
};
export default AccessToken;

View File

@@ -0,0 +1,89 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../../components/InputField';
import { TextInputType } from '../../constants';
const DatabaseCredentials = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Database Credentials
</Text>
<Stack spacing={6} padding="3% 0">
<Text fontStyle="italic" fontSize="sm" color="blackAlpha.500" mt={3}>
Note: Database related environment variables cannot be updated from
dashboard. Please use .env file or OS environment variables to update
it.
</Text>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_NAME}
isDisabled={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase Type:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_TYPE}
isDisabled={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase URL:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_URL}
isDisabled={true}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default DatabaseCredentials;

View File

@@ -0,0 +1,35 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../../components/InputField';
import { ArrayInputType } from '../../constants';
const DomainWhiteListing = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Domain White Listing
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Allowed Origins:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.ALLOWED_ORIGINS}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default DomainWhiteListing;

View File

@@ -0,0 +1,150 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../../components/InputField';
import { TextInputType, HiddenInputType } from '../../constants';
const EmailConfigurations = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Email Configurations
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Host:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_HOST}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Port:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_PORT}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Local Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_LOCAL_NAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Username:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_USERNAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Password:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.SMTP_PASSWORD}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">From Email:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SENDER_EMAIL}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Sender Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SENDER_NAME}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default EmailConfigurations;

View File

@@ -0,0 +1,229 @@
import React from 'react';
import { Divider, Flex, Stack, Text } from '@chakra-ui/react';
import InputField from '../InputField';
import { SwitchInputType } from '../../constants';
const Features = ({ variables, setVariables }: any) => {
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Features
</Text>
<Stack spacing={6}>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Login Page:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Email Verification:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Magic Login Link:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Email Basic Authentication:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Mobile Basic Authentication:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MOBILE_BASIC_AUTHENTICATION}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Sign Up:</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_SIGN_UP}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Strong Password:</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_STRONG_PASSWORD}
hasReversedValue
/>
</Flex>
</Flex>
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Multi Factor Authentication (MFA):</Text>
<Text fontSize="x-small">
Note: Enabling this will ignore Enforcing MFA shown below and will
also ignore the user MFA setting.
</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MULTI_FACTOR_AUTHENTICATION}
hasReversedValue
/>
</Flex>
</Flex>
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && (
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Time Based OTP (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 w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">
Enforce Multi Factor Authentication (MFA):
</Text>
<Text fontSize="x-small">
Note: If you disable enforcing after it was enabled, it will still
keep MFA enabled for older users.
</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.ENFORCE_MULTI_FACTOR_AUTHENTICATION}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Playground:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_PLAYGROUND}
hasReversedValue
/>
</Flex>
</Flex>
</Stack>
<Divider paddingY={5} />
<Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
Cookie Security Features
</Text>
<Stack spacing={6}>
<Flex>
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Use Secure App Cookie:</Text>
<Text fontSize="x-small">
Note: If you set this to insecure, it will set{' '}
<code>sameSite</code> property of cookie to <code>lax</code> mode
</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.APP_COOKIE_SECURE}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Use Secure Admin Cookie:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.ADMIN_COOKIE_SECURE}
/>
</Flex>
</Flex>
</Stack>
</div>
);
};
export default Features;

View File

@@ -0,0 +1,200 @@
import React from 'react';
import {
Flex,
Stack,
Center,
Text,
useMediaQuery,
Button,
useToast,
} from '@chakra-ui/react';
import {
HiddenInputType,
TextInputType,
TextAreaInputType,
} from '../../constants';
import GenerateKeysModal from '../GenerateKeysModal';
import InputField from '../InputField';
import { copyTextToClipboard } from '../../utils';
const JSTConfigurations = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
SelectInputType,
getData,
HMACEncryptionType,
RSAEncryptionType,
ECDSAEncryptionType,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
const toast = useToast();
const copyJSON = async () => {
try {
await copyTextToClipboard(
JSON.stringify({
type: variables.JWT_TYPE,
key: variables.JWT_PUBLIC_KEY || variables.JWT_SECRET,
}),
);
toast({
title: `JWT config copied successfully`,
isClosable: true,
status: 'success',
position: 'top-right',
});
} catch (err) {
console.error({
message: `Failed to copy JWT config`,
error: err,
});
toast({
title: `Failed to copy JWT config`,
isClosable: true,
status: 'error',
position: 'top-right',
});
}
};
return (
<div>
<Flex
borderRadius={5}
width="100%"
justifyContent="space-between"
alignItems="center"
paddingTop="2%"
>
<Text
fontSize={isNotSmallerScreen ? 'md' : 'sm'}
fontWeight="bold"
mb={5}
>
JWT (JSON Web Tokens) Configurations
</Text>
<Flex mb={7}>
<Button
colorScheme="blue"
h="1.75rem"
size="sm"
variant="ghost"
onClick={copyJSON}
>
Copy As JSON Config
</Button>
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
</Flex>
</Flex>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Type:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={SelectInputType}
value={SelectInputType}
options={{
...HMACEncryptionType,
...RSAEncryptionType,
...ECDSAEncryptionType,
}}
/>
</Flex>
</Flex>
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Secret</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.JWT_SECRET}
/>
</Center>
</Flex>
) : (
<>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Public Key</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
placeholder="Add public key here"
minH="25vh"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Private Key</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
placeholder="Add private key here"
minH="25vh"
/>
</Center>
</Flex>
</>
)}
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm" orientation="vertical">
JWT Role Claim:
</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.JWT_ROLE_CLAIM}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default JSTConfigurations;

View File

@@ -0,0 +1,525 @@
import React from 'react';
import InputField from '../InputField';
import {
Flex,
Stack,
Center,
Text,
Box,
Divider,
useMediaQuery,
} from '@chakra-ui/react';
import {
FaGoogle,
FaGithub,
FaFacebookF,
FaLinkedin,
FaApple,
FaTwitter,
FaMicrosoft,
FaTwitch,
FaDiscord,
} from 'react-icons/fa';
import {
TextInputType,
HiddenInputType,
ResponseModes,
ResponseTypes,
SelectInputType,
} from '../../constants';
const OAuthConfig = ({
envVariables,
setVariables,
fieldVisibility,
setFieldVisibility,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:667px)');
return (
<div>
<Box>
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={6}>
Authorizer Config
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Client ID</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={envVariables}
setVariables={() => {}}
inputType={TextInputType.CLIENT_ID}
placeholder="Client ID"
readOnly={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Client Secret</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.CLIENT_SECRET}
placeholder="Client Secret"
readOnly={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Response Type:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_TYPE}
value={SelectInputType}
options={ResponseTypes}
/>
</Flex>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Response Mode:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_MODE}
value={SelectInputType}
options={ResponseModes}
/>
</Flex>
</Flex>
</Stack>
<Divider mt={5} mb={2} color="blackAlpha.700" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}>
Social Media Logins
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #ff3e30"
borderRadius="5px"
>
<FaGoogle style={{ color: '#ff3e30' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.GOOGLE_CLIENT_ID}
placeholder="Google Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
placeholder="Google Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #171515"
borderRadius="5px"
>
<FaGithub style={{ color: '#171515' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.GITHUB_CLIENT_ID}
placeholder="Github Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
placeholder="Github Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaFacebookF style={{ color: '#3b5998' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.FACEBOOK_CLIENT_ID}
placeholder="Facebook Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
placeholder="Facebook Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaLinkedin style={{ color: '#3b5998' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.LINKEDIN_CLIENT_ID}
placeholder="LinkedIn Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.LINKEDIN_CLIENT_SECRET}
placeholder="LinkedIn Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaApple style={{ color: '#3b5998' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.APPLE_CLIENT_ID}
placeholder="Apple Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.APPLE_CLIENT_SECRET}
placeholder="Apple Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaDiscord style={{ color: '#7289da' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.DISCORD_CLIENT_ID}
placeholder="Discord Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.DISCORD_CLIENT_SECRET}
placeholder="Discord Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaTwitter />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.TWITTER_CLIENT_ID}
placeholder="Twitter Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.TWITTER_CLIENT_SECRET}
placeholder="Twitter Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaMicrosoft />
</Center>
<Center
w={isNotSmallerScreen ? '35%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID}
placeholder="Microsoft Active Directory TenantID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '35%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.MICROSOFT_CLIENT_ID}
placeholder="Microsoft Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.MICROSOFT_CLIENT_SECRET}
placeholder="Microsoft Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaTwitch />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.TWITCH_CLIENT_ID}
placeholder="Twitch Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.TWITCH_CLIENT_SECRET}
placeholder="Twitch Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<img
src="https://authorizer.dev/_next/image?url=%2Fimages%2Froblox.png&w=25&q=25"
alt="Roblox"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.ROBLOX_CLIENT_ID}
placeholder="Roblox Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.ROBLOX_CLIENT_SECRET}
placeholder="Roblox Client Secret"
/>
</Center>
</Flex>
</Stack>
</Box>
</div>
);
};
export default OAuthConfig;

View File

@@ -0,0 +1,60 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../InputField';
import { TextInputType } from '../../constants';
const OrganizationInfo = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Organization Information
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Organization Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ORGANIZATION_NAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Organization Logo:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ORGANIZATION_LOGO}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default OrganizationInfo;

View File

@@ -0,0 +1,68 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import { ArrayInputType } from '../../constants';
import InputField from '../InputField';
const Roles = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Roles
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
overflow="hidden"
>
<InputField
borderRadius={7}
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.ROLES}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.DEFAULT_ROLES}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Protected Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.PROTECTED_ROLES}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default Roles;

View File

@@ -0,0 +1,138 @@
import React from 'react';
import {
Flex,
Stack,
Center,
Text,
Input,
InputGroup,
InputRightElement,
useMediaQuery,
} from '@chakra-ui/react';
import { FaRegEyeSlash, FaRegEye } from 'react-icons/fa';
import InputField from '../InputField';
import { HiddenInputType } from '../../constants';
const SecurityAdminSecret = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
validateAdminSecretHandler,
adminSecret,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Security (Admin Secret)
</Text>
<Stack
spacing={6}
padding="0 5%"
marginTop="3%"
border="1px solid #ff7875"
borderRadius="5px"
>
<Flex
marginTop={isNotSmallerScreen ? '3%' : '5%'}
direction={isNotSmallerScreen ? 'row' : 'column'}
>
<Flex
mt={3}
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Old Admin Secret:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputGroup size="sm">
<Input
borderRadius={5}
size="sm"
placeholder="Enter Old Admin Secret"
value={adminSecret.value as string}
onChange={(event: any) => validateAdminSecretHandler(event)}
type={
!fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET]
? 'password'
: 'text'
}
/>
<InputRightElement
right="5px"
children={
<Flex>
{fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: false,
})
}
>
<FaRegEyeSlash color="#bfbfbf" />
</Center>
) : (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: true,
})
}
>
<FaRegEye color="#bfbfbf" />
</Center>
)}
</Flex>
}
/>
</InputGroup>
</Center>
</Flex>
<Flex
paddingBottom="3%"
direction={isNotSmallerScreen ? 'row' : 'column'}
>
<Flex
w={isNotSmallerScreen ? '30%' : '50%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">New Admin Secret:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
mb={3}
variables={variables}
setVariables={setVariables}
inputType={HiddenInputType.ADMIN_SECRET}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
isDisabled={adminSecret.disableInputField}
placeholder="Enter New Admin Secret"
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default SecurityAdminSecret;

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../InputField';
const SessionStorage = ({ variables, setVariables, RedisURL }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Session Storage
</Text>
<Text fontStyle="italic" fontSize="sm" color="blackAlpha.500" mt={3}>
Note: Redis related environment variables cannot be updated from
dashboard. Please use .env file or OS environment variables to update
it.
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Redis URL:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
disabled
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={RedisURL}
placeholder="Redis URL"
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default SessionStorage;

View File

@@ -0,0 +1,247 @@
import React from 'react';
import {
Button,
Center,
Flex,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
Input,
Spinner,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa';
import {
ECDSAEncryptionType,
HMACEncryptionType,
RSAEncryptionType,
SelectInputType,
TextAreaInputType,
} from '../constants';
import InputField from './InputField';
import { GenerateKeys, UpdateEnvVariables } from '../graphql/mutation';
interface propTypes {
jwtType: string;
getData: Function;
}
interface stateVarTypes {
JWT_TYPE: string;
JWT_SECRET: string;
JWT_PRIVATE_KEY: string;
JWT_PUBLIC_KEY: string;
}
const initState: stateVarTypes = {
JWT_TYPE: '',
JWT_SECRET: '',
JWT_PRIVATE_KEY: '',
JWT_PUBLIC_KEY: '',
};
const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [stateVariables, setStateVariables] = React.useState<stateVarTypes>({
...initState,
});
const [isLoading, setIsLoading] = React.useState(false);
React.useEffect(() => {
if (isOpen) {
setStateVariables({ ...initState, JWT_TYPE: jwtType });
}
}, [isOpen]);
const fetchKeys = async () => {
setIsLoading(true);
try {
const res = await client
.mutation(GenerateKeys, { params: { type: stateVariables.JWT_TYPE } })
.toPromise();
if (res?.error) {
toast({
title: 'Error occurred generating jwt keys',
isClosable: true,
status: 'error',
position: 'top-right',
});
closeHandler();
} else {
setStateVariables({
...stateVariables,
JWT_SECRET: res?.data?._generate_jwt_keys?.secret || '',
JWT_PRIVATE_KEY: res?.data?._generate_jwt_keys?.private_key || '',
JWT_PUBLIC_KEY: res?.data?._generate_jwt_keys?.public_key || '',
});
}
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
React.useEffect(() => {
if (isOpen && stateVariables.JWT_TYPE) {
fetchKeys();
}
}, [stateVariables.JWT_TYPE]);
const saveHandler = async () => {
const res = await client
.mutation(UpdateEnvVariables, { params: { ...stateVariables } })
.toPromise();
if (res.error) {
toast({
title: 'Error occurred setting jwt keys',
isClosable: true,
status: 'error',
position: 'top-right',
});
return;
}
toast({
title: 'JWT keys updated successfully',
isClosable: true,
status: 'success',
position: 'top-right',
});
closeHandler();
};
const closeHandler = () => {
setStateVariables({ ...initState });
getData();
onClose();
};
return (
<>
<Button
colorScheme="blue"
h="1.75rem"
size="sm"
variant="ghost"
onClick={onOpen}
>
Generate new keys
</Button>
<Modal isOpen={isOpen} onClose={closeHandler}>
<ModalOverlay />
<ModalContent>
<ModalHeader>New JWT keys</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Type:</Text>
</Flex>
<InputField
variables={stateVariables}
setVariables={setStateVariables}
inputType={SelectInputType.JWT_TYPE}
value={SelectInputType.JWT_TYPE}
options={{
...HMACEncryptionType,
...RSAEncryptionType,
...ECDSAEncryptionType,
}}
/>
</Flex>
{isLoading ? (
<Center minH="25vh">
<Spinner />
</Center>
) : (
<>
{Object.values(HMACEncryptionType).includes(
stateVariables.JWT_TYPE,
) ? (
<Flex marginTop="8">
<Flex w="23%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Secret</Text>
</Flex>
<Center w="77%">
<Input
size="sm"
value={stateVariables.JWT_SECRET}
onChange={(event: any) =>
setStateVariables({
...stateVariables,
JWT_SECRET: event.target.value,
})
}
readOnly
/>
</Center>
</Flex>
) : (
<>
<Flex marginTop="8">
<Flex w="23%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Public Key</Text>
</Flex>
<Center w="77%">
<InputField
variables={stateVariables}
setVariables={setStateVariables}
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
placeholder="Add public key here"
minH="25vh"
readOnly
/>
</Center>
</Flex>
<Flex marginTop="8">
<Flex w="23%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Private Key</Text>
</Flex>
<Center w="77%">
<InputField
variables={stateVariables}
setVariables={setStateVariables}
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
placeholder="Add private key here"
minH="25vh"
readOnly
/>
</Center>
</Flex>
</>
)}
</>
)}
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaSave />}
colorScheme="blue"
variant="solid"
onClick={saveHandler}
isDisabled={isLoading}
>
<Center h="100%" pt="5%">
Apply
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default GenerateKeysModal;

View File

@@ -0,0 +1,436 @@
import React, { useState } from 'react';
import {
Box,
Flex,
Input,
Center,
InputGroup,
InputRightElement,
Tag,
TagLabel,
TagRightIcon,
Select,
Textarea,
Switch,
Text,
MenuButton,
MenuList,
MenuItemOption,
MenuOptionGroup,
Button,
Menu,
} from '@chakra-ui/react';
import {
FaRegClone,
FaRegEye,
FaRegEyeSlash,
FaPlus,
FaTimes,
FaAngleDown,
} from 'react-icons/fa';
import {
ArrayInputOperations,
ArrayInputType,
SelectInputType,
HiddenInputType,
TextInputType,
TextAreaInputType,
SwitchInputType,
DateInputType,
MultiSelectInputType,
} from '../constants';
import { copyTextToClipboard } from '../utils';
const InputField = ({
inputType,
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
availableRoles,
// This prop is added to improve the user experience for the boolean ENV variable having `DISABLE_` prefix, as those values need to be considered in inverted form.
hasReversedValue,
...downshiftProps
}: any) => {
const props = {
size: 'sm',
...downshiftProps,
};
const [availableUserRoles, setAvailableUserRoles] =
useState<string[]>(availableRoles);
const [inputFieldVisibility, setInputFieldVisibility] = useState<
Record<string, boolean>
>({
ROLES: false,
DEFAULT_ROLES: false,
PROTECTED_ROLES: false,
ALLOWED_ORIGINS: false,
roles: false,
});
const [inputData, setInputData] = useState<Record<string, string>>({
ROLES: '',
DEFAULT_ROLES: '',
PROTECTED_ROLES: '',
ALLOWED_ORIGINS: '',
roles: '',
});
const updateInputHandler = (
type: string,
operation: any,
role: string = '',
) => {
if (operation === ArrayInputOperations.APPEND) {
if (inputData[type] !== '') {
setVariables({
...variables,
[type]: [...variables[type], inputData[type]],
});
setInputData({ ...inputData, [type]: '' });
}
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
}
if (operation === ArrayInputOperations.REMOVE) {
let updatedEnvVars = variables[type].filter(
(item: string) => item !== role,
);
setVariables({
...variables,
[type]: updatedEnvVars,
});
}
};
if (Object.values(TextInputType).includes(inputType)) {
return (
<InputGroup size="sm">
<Input
{...props}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(
event: Event & {
target: HTMLInputElement;
},
) =>
setVariables({
...variables,
[inputType]: event.target.value,
})
}
/>
<InputRightElement
children={<FaRegClone color="#bfbfbf" />}
cursor="pointer"
onClick={() => copyTextToClipboard(variables[inputType])}
/>
</InputGroup>
);
}
if (Object.values(HiddenInputType).includes(inputType)) {
return (
<InputGroup size="sm">
<Input
{...props}
value={variables[inputType] || ''}
onChange={(
event: Event & {
target: HTMLInputElement;
},
) =>
setVariables({
...variables,
[inputType]: event.target.value,
})
}
type={!fieldVisibility[inputType] ? 'password' : 'text'}
/>
<InputRightElement
right="15px"
children={
<Flex>
{fieldVisibility[inputType] ? (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[inputType]: false,
})
}
>
<FaRegEyeSlash color="#bfbfbf" />
</Center>
) : (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[inputType]: true,
})
}
>
<FaRegEye color="#bfbfbf" />
</Center>
)}
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() => copyTextToClipboard(variables[inputType])}
>
<FaRegClone color="#bfbfbf" />
</Center>
</Flex>
}
/>
</InputGroup>
);
}
if (Object.values(ArrayInputType).includes(inputType)) {
return (
<Flex
border="1px solid #e2e8f0"
w="100%"
borderRadius={5}
paddingTop="0.5%"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
overflowY="hidden"
justifyContent="start"
alignItems="center"
>
{variables[inputType].map((role: string, index: number) => (
<Box key={index} margin="0.5%" role="group">
<Tag
size="sm"
variant="outline"
colorScheme="gray"
minW="fit-content"
>
<TagLabel cursor="default">{role}</TagLabel>
<TagRightIcon
boxSize="12px"
as={FaTimes}
display="none"
cursor="pointer"
_groupHover={{ display: 'block' }}
onClick={() =>
updateInputHandler(
inputType,
ArrayInputOperations.REMOVE,
role,
)
}
/>
</Tag>
</Box>
))}
{inputFieldVisibility[inputType] ? (
<Box ml="1%" mb="0.75%">
<Input
type="text"
size="xs"
minW="150px"
placeholder="add a new value"
value={inputData[inputType] || ''}
onChange={(e: any) => {
setInputData({ ...inputData, [inputType]: e.target.value });
}}
onBlur={() =>
updateInputHandler(inputType, ArrayInputOperations.APPEND)
}
onKeyPress={(event) => {
if (event.key === 'Enter') {
updateInputHandler(inputType, ArrayInputOperations.APPEND);
}
}}
/>
</Box>
) : (
<Box
marginLeft="0.5%"
cursor="pointer"
onClick={() =>
setInputFieldVisibility({
...inputFieldVisibility,
[inputType]: true,
})
}
>
<Tag
size="sm"
variant="outline"
colorScheme="gray"
minW="fit-content"
>
<FaPlus />
</Tag>
</Box>
)}
</Flex>
);
}
if (Object.values(SelectInputType).includes(inputType)) {
const { options, ...rest } = props;
return (
<Select
size="sm"
{...rest}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(e) =>
setVariables({ ...variables, [inputType]: e.target.value })
}
>
{Object.entries(options).map(([key, value]: any) => (
<option value={value} key={key}>
{key}
</option>
))}
</Select>
);
}
if (Object.values(MultiSelectInputType).includes(inputType)) {
return (
<Flex w="100%" style={{ position: 'relative' }}>
<Flex
border="1px solid #e2e8f0"
w="100%"
borderRadius="var(--chakra-radii-sm)"
p="1% 0 0 2.5%"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
overflowY="hidden"
justifyContent="space-between"
alignItems="center"
>
<Flex justifyContent="start" alignItems="center" w="100%" wrap="wrap">
{variables[inputType].map((role: string, index: number) => (
<Box key={index} margin="0.5%" role="group">
<Tag
size="sm"
variant="outline"
colorScheme="gray"
minW="fit-content"
>
<TagLabel cursor="default">{role}</TagLabel>
<TagRightIcon
boxSize="12px"
as={FaTimes}
display="none"
cursor="pointer"
_groupHover={{ display: 'block' }}
onClick={() =>
updateInputHandler(
inputType,
ArrayInputOperations.REMOVE,
role,
)
}
/>
</Tag>
</Box>
))}
</Flex>
<Menu matchWidth={true}>
<MenuButton px="10px" py="7.5px">
<FaAngleDown />
</MenuButton>
<MenuList
position="absolute"
top="0"
right="0"
zIndex="10"
maxH="150"
overflowX="scroll"
>
<MenuOptionGroup
title={undefined}
value={variables[inputType]}
type="checkbox"
onChange={(values: string[] | string) => {
setVariables({
...variables,
[inputType]: values,
});
}}
>
{availableUserRoles.map((role) => {
return (
<MenuItemOption
key={`multiselect-menu-${role}`}
value={role}
>
{role}
</MenuItemOption>
);
})}
</MenuOptionGroup>
</MenuList>
</Menu>
</Flex>
</Flex>
);
}
if (Object.values(TextAreaInputType).includes(inputType)) {
return (
<Textarea
{...props}
size="lg"
fontSize={14}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(
event: Event & {
target: HTMLInputElement;
},
) =>
setVariables({
...variables,
[inputType]: event.target.value,
})
}
/>
);
}
if (Object.values(SwitchInputType).includes(inputType)) {
return (
<Flex w="25%" justifyContent="space-between">
<Text h="75%" fontWeight="bold" marginRight="2">
Off
</Text>
<Switch
size="md"
isChecked={
hasReversedValue ? !variables[inputType] : variables[inputType]
}
onChange={() => {
setVariables({
...variables,
[inputType]: !variables[inputType],
});
}}
/>
<Text h="75%" fontWeight="bold" marginLeft="2">
On
</Text>
</Flex>
);
}
if (Object.values(DateInputType).includes(inputType)) {
return (
<Flex border="1px solid #e2e8f0" w="100%" h="33px" padding="1%">
<input
type="date"
style={{ width: '100%', paddingLeft: '2.5%' }}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(e) =>
setVariables({ ...variables, [inputType]: e.target.value })
}
/>
</Flex>
);
}
return null;
};
export default InputField;

View File

@@ -0,0 +1,385 @@
import React, { useState, useCallback, useEffect } from 'react';
import {
Button,
Center,
Flex,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
useToast,
Tabs,
TabList,
Tab,
TabPanels,
TabPanel,
InputGroup,
Input,
InputRightElement,
Text,
Link,
Tooltip,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaUserPlus, FaMinusCircle, FaPlus, FaUpload } from 'react-icons/fa';
import { useDropzone } from 'react-dropzone';
import { validateEmail, validateURI } from '../utils';
import { InviteMembers } from '../graphql/mutation';
import { ArrayInputOperations } from '../constants';
import parseCSV from '../utils/parseCSV';
interface stateDataTypes {
value: string;
isInvalid: boolean;
}
interface requestParamTypes {
emails: string[];
redirect_uri?: string;
}
const initData: stateDataTypes = {
value: '',
isInvalid: false,
};
const InviteMembersModal = ({
updateUserList,
disabled = true,
}: {
updateUserList: Function;
disabled: boolean;
}) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [tabIndex, setTabIndex] = useState<number>(0);
const [redirectURI, setRedirectURI] = useState<stateDataTypes>({
...initData,
});
const [emails, setEmails] = useState<stateDataTypes[]>([{ ...initData }]);
const [disableSendButton, setDisableSendButton] = useState<boolean>(false);
const [loading, setLoading] = React.useState<boolean>(false);
useEffect(() => {
if (redirectURI.isInvalid) {
setDisableSendButton(true);
} else if (emails.some((emailData) => emailData.isInvalid)) {
setDisableSendButton(true);
} else {
setDisableSendButton(false);
}
}, [redirectURI, emails]);
useEffect(() => {
return () => {
setRedirectURI({ ...initData });
setEmails([{ ...initData }]);
};
}, []);
const sendInviteHandler = async () => {
setLoading(true);
try {
const emailList = emails
.filter((emailData) => !emailData.isInvalid)
.map((emailData) => emailData.value);
const params: requestParamTypes = {
emails: emailList,
};
if (redirectURI.value !== '' && !redirectURI.isInvalid) {
params.redirect_uri = redirectURI.value;
}
if (emailList.length > 0) {
const res = await client
.mutation(InviteMembers, {
params,
})
.toPromise();
if (res.error) {
throw new Error('Internal server error');
return;
}
toast({
title: 'Invites sent successfully!',
isClosable: true,
status: 'success',
position: 'top-right',
});
setLoading(false);
updateUserList();
} else {
throw new Error('Please add emails');
}
} catch (error: any) {
toast({
title: error?.message || 'Error occurred, try again!',
isClosable: true,
status: 'error',
position: 'top-right',
});
setLoading(false);
}
closeModalHandler();
};
const updateEmailListHandler = (operation: string, index: number = 0) => {
switch (operation) {
case ArrayInputOperations.APPEND:
setEmails([...emails, { ...initData }]);
break;
case ArrayInputOperations.REMOVE:
const updatedEmailList = [...emails];
updatedEmailList.splice(index, 1);
setEmails(updatedEmailList);
break;
default:
break;
}
};
const inputChangeHandler = (value: string, index: number) => {
const updatedEmailList = [...emails];
updatedEmailList[index].value = value;
updatedEmailList[index].isInvalid = !validateEmail(value);
setEmails(updatedEmailList);
};
const changeTabsHandler = (index: number) => {
setTabIndex(index);
};
const onDrop = useCallback(async (acceptedFiles) => {
const result = await parseCSV(acceptedFiles[0], ',');
setEmails(result);
changeTabsHandler(0);
}, []);
const setRedirectURIHandler = (value: string) => {
const updatedRedirectURI: stateDataTypes = {
value: '',
isInvalid: false,
};
updatedRedirectURI.value = value;
updatedRedirectURI.isInvalid = !validateURI(value);
setRedirectURI(updatedRedirectURI);
};
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: 'text/csv',
});
const closeModalHandler = () => {
setRedirectURI({
value: '',
isInvalid: false,
});
setEmails([
{
value: '',
isInvalid: false,
},
]);
onClose();
};
return (
<>
<Button
leftIcon={<FaUserPlus />}
colorScheme="blue"
variant="solid"
onClick={onOpen}
isDisabled={disabled}
size="sm"
>
<Center h="100%">
{disabled ? (
<Tooltip
mr={8}
mt={1}
hasArrow
bg="gray.300"
color="black"
label="Email verification is disabled, refer to 'Features' tab within 'Environment' to enable it."
>
Invite Members
</Tooltip>
) : (
'Invite Members'
)}
</Center>{' '}
</Button>
<Modal isOpen={isOpen} onClose={closeModalHandler} size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>Invite Members</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Tabs
isFitted
variant="enclosed"
index={tabIndex}
onChange={changeTabsHandler}
>
<TabList>
<Tab>Enter emails</Tab>
<Tab>Upload CSV</Tab>
</TabList>
<TabPanels
border="1px"
borderTop="0"
borderBottomRadius="5px"
borderColor="inherit"
>
<TabPanel>
<Flex flexDirection="column">
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="2%"
>
<Flex marginLeft="2.5%">Redirect URI</Flex>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<InputGroup size="md" marginBottom="2.5%">
<Input
pr="4.5rem"
type="text"
placeholder="https://domain.com/sign-up"
value={redirectURI.value}
isInvalid={redirectURI.isInvalid}
onChange={(e) =>
setRedirectURIHandler(e.currentTarget.value)
}
/>
</InputGroup>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<Flex marginLeft="2.5%">Emails</Flex>
<Flex>
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
h="1.75rem"
size="sm"
variant="ghost"
onClick={() =>
updateEmailListHandler(ArrayInputOperations.APPEND)
}
>
Add more emails
</Button>
</Flex>
</Flex>
<Flex flexDirection="column" maxH={250} overflowY="scroll">
{emails.map((emailData, index) => (
<Flex
key={`email-data-${index}`}
justifyContent="center"
alignItems="center"
>
<InputGroup size="md" marginBottom="2.5%">
<Input
pr="4.5rem"
type="text"
placeholder="name@domain.com"
value={emailData.value}
isInvalid={emailData.isInvalid}
onChange={(e) =>
inputChangeHandler(e.currentTarget.value, index)
}
/>
<InputRightElement width="3rem">
<Button
h="1.75rem"
size="sm"
colorScheme="blackAlpha"
variant="ghost"
onClick={() =>
updateEmailListHandler(
ArrayInputOperations.REMOVE,
index,
)
}
>
<FaMinusCircle />
</Button>
</InputRightElement>
</InputGroup>
</Flex>
))}
</Flex>
</Flex>
</TabPanel>
<TabPanel>
<Flex
justify="center"
align="center"
textAlign="center"
bg="#f0f0f0"
h={230}
p={50}
m={2}
borderRadius={5}
{...getRootProps()}
>
<input {...getInputProps()} />
{isDragActive ? (
<Text>Drop the files here...</Text>
) : (
<Flex
flexDirection="column"
justifyContent="center"
alignItems="center"
>
<Center boxSize="20" color="blackAlpha.500">
<FaUpload fontSize="40" />
</Center>
<Text>
Drag 'n' drop the csv file here, or click to select.
</Text>
<Text size="xs">
Download{' '}
<Link
href={`/dashboard/public/sample.csv`}
download="sample.csv"
color="blue.600"
onClick={(e) => e.stopPropagation()}
>
{' '}
sample.csv
</Link>{' '}
and modify it.{' '}
</Text>
</Flex>
)}
</Flex>
</TabPanel>
</TabPanels>
</Tabs>
</ModalBody>
<ModalFooter>
<Button
colorScheme="blue"
variant="solid"
onClick={sendInviteHandler}
isDisabled={disableSendButton || loading}
>
<Center h="100%" pt="5%">
Send
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default InviteMembersModal;

View File

@@ -0,0 +1,368 @@
import React, { Fragment, ReactNode } from 'react';
import {
IconButton,
Box,
CloseButton,
Flex,
Image,
HStack,
VStack,
Icon,
useColorModeValue,
Link,
Text,
BoxProps,
FlexProps,
Menu,
MenuButton,
MenuItem,
MenuList,
Accordion,
AccordionButton,
AccordionPanel,
AccordionItem,
useMediaQuery,
} from '@chakra-ui/react';
import {
FiUser,
FiCode,
FiSettings,
FiMenu,
FiUsers,
FiChevronDown,
FiLink,
FiFileText,
} from 'react-icons/fi';
import { BiCustomize } from 'react-icons/bi';
import { AiOutlineKey } from 'react-icons/ai';
import { SiOpenaccess, SiJsonwebtokens } from 'react-icons/si';
import { MdSecurity } from 'react-icons/md';
import { RiDatabase2Line } from 'react-icons/ri';
import { BsCheck2Circle } from 'react-icons/bs';
import { HiOutlineMail, HiOutlineOfficeBuilding } from 'react-icons/hi';
import { IconType } from 'react-icons';
import { ReactText } from 'react';
import { useMutation, useQuery } from 'urql';
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
import { AdminLogout } from '../graphql/mutation';
import { MetaQuery } from '../graphql/queries';
interface SubRoutes {
name: string;
icon: IconType;
route: string;
}
interface LinkItemProps {
name: string;
icon: IconType;
route: string;
subRoutes?: SubRoutes[];
}
const LinkItems: Array<LinkItemProps> = [
{
name: 'Environment ',
icon: FiSettings,
route: '/',
subRoutes: [
{
name: 'OAuth Config',
icon: AiOutlineKey,
route: '/oauth-setting',
},
{ name: 'Roles', icon: FiUser, route: '/roles' },
{
name: 'JWT Secrets',
icon: SiJsonwebtokens,
route: '/jwt-config',
},
{
name: 'Session Storage',
icon: RiDatabase2Line,
route: '/session-storage',
},
{
name: 'Email Configurations',
icon: HiOutlineMail,
route: '/email-config',
},
{
name: 'Domain White Listing',
icon: BsCheck2Circle,
route: '/whitelist-variables',
},
{
name: 'Organization Info',
icon: HiOutlineOfficeBuilding,
route: '/organization-info',
},
{ name: 'Access Token', icon: SiOpenaccess, route: '/access-token' },
{
name: 'Features',
icon: BiCustomize,
route: '/features',
},
{ name: 'Database', icon: RiDatabase2Line, route: '/db-cred' },
{
name: ' Security',
icon: MdSecurity,
route: '/admin-secret',
},
],
},
{ name: 'Users', icon: FiUsers, route: '/users' },
{ name: 'Webhooks', icon: FiLink, route: '/webhooks' },
{ name: 'Email Templates', icon: FiFileText, route: '/email-templates' },
];
interface SidebarProps extends BoxProps {
onClose: () => void;
}
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
const { pathname } = useLocation();
const [{ data }] = useQuery({ query: MetaQuery });
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<Box
transition="3s ease"
bg={useColorModeValue('white', 'gray.900')}
borderRight="1px"
borderRightColor={useColorModeValue('gray.200', 'gray.700')}
w={{ base: 'full', md: '64' }}
pos="fixed"
h="full"
{...rest}
>
<Flex
h="20"
alignItems="center"
mx="18"
justifyContent="space-between"
flexDirection="row"
>
<NavLink to="/">
<Flex alignItems="center" mt="6">
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
height="36px"
/>
<Text fontSize="large" ml="2" letterSpacing="3">
AUTHORIZER
</Text>
</Flex>
</NavLink>
<CloseButton display={{ base: 'flex', md: 'none' }} onClick={onClose} />
</Flex>
<Accordion defaultIndex={[0]} allowMultiple>
<AccordionItem textAlign="center" border="none" w="100%">
{LinkItems.map((link) =>
link?.subRoutes ? (
<div key={link.name}>
<AccordionButton _focus={{ boxShadow: 'none' }}>
<Text as="div" fontSize="md">
<NavItem
icon={link.icon}
color={pathname === link.route ? 'blue.500' : ''}
style={{ outline: 'unset' }}
height={12}
ml={-1}
mb={isNotSmallerScreen ? -1 : -4}
w={isNotSmallerScreen ? '100%' : '310%'}
>
<Fragment>
{link.name}
<Box display={{ base: 'none', md: 'flex' }} ml={12}>
<FiChevronDown />
</Box>
</Fragment>
</NavItem>
</Text>
</AccordionButton>
<AccordionPanel>
{link.subRoutes?.map((sublink) => (
<NavLink
key={sublink.name}
to={sublink.route}
onClick={onClose}
>
{' '}
<Text as="div" fontSize="xs" ml={2}>
<NavItem
icon={sublink.icon}
color={pathname === sublink.route ? 'blue.500' : ''}
height={8}
>
{sublink.name}
</NavItem>{' '}
</Text>
</NavLink>
))}
</AccordionPanel>
</div>
) : (
<NavLink key={link.name} to={link.route}>
{' '}
<Text as="div" fontSize="md" w="100%" mt={-2}>
<NavItem
icon={link.icon}
color={pathname === link.route ? 'blue.500' : ''}
height={12}
onClick={onClose}
>
{link.name}
</NavItem>{' '}
</Text>
</NavLink>
),
)}
<Link
href="/playground"
target="_blank"
style={{
textDecoration: 'none',
}}
_focus={{ _boxShadow: 'none' }}
>
<NavItem icon={FiCode}>API Playground</NavItem>
</Link>
</AccordionItem>
</Accordion>
{data?.meta?.version && (
<Flex alignContent="center">
{' '}
<Text
color="gray.400"
fontSize="sm"
textAlign="center"
position="absolute"
bottom="5"
left="7"
>
Current Version: {data.meta.version}
</Text>
</Flex>
)}
</Box>
);
};
interface NavItemProps extends FlexProps {
icon: IconType;
children: ReactText | JSX.Element | JSX.Element[];
}
export const NavItem = ({ icon, children, ...rest }: NavItemProps) => {
return (
<Flex
align="center"
p="3"
mx="3"
borderRadius="md"
role="group"
cursor="pointer"
_hover={{
bg: 'blue.500',
color: 'white',
}}
{...rest}
>
{icon && (
<Icon
mr="4"
fontSize="16"
_groupHover={{
color: 'white',
}}
as={icon}
/>
)}
{children}
</Flex>
);
};
interface MobileProps extends FlexProps {
onOpen: () => void;
}
export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
const [_, logout] = useMutation(AdminLogout);
const { setIsLoggedIn } = useAuthContext();
const navigate = useNavigate();
const handleLogout = async () => {
await logout();
setIsLoggedIn(false);
navigate('/', { replace: true });
};
return (
<Flex
ml={{ base: 0, md: 64 }}
px={{ base: 4, md: 4 }}
height="20"
position="fixed"
right="0"
left="0"
alignItems="center"
bg={useColorModeValue('white', 'gray.900')}
borderBottomWidth="1px"
borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
justifyContent={{ base: 'space-between', md: 'flex-end' }}
zIndex={99}
{...rest}
>
<IconButton
display={{ base: 'flex', md: 'none' }}
onClick={onOpen}
variant="outline"
aria-label="open menu"
icon={<FiMenu />}
/>
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
height="36px"
display={{ base: 'flex', md: 'none' }}
/>
<HStack spacing={{ base: '0', md: '6' }}>
<Flex alignItems={'center'}>
<Menu>
<MenuButton
py={2}
transition="all 0.3s"
_focus={{ boxShadow: 'none' }}
>
<HStack mr={5}>
<FiUser />
<VStack
display={{ base: 'none', md: 'flex' }}
alignItems="flex-start"
spacing="1px"
ml="2"
>
<Text fontSize="sm">Admin</Text>
</VStack>
<Box display={{ base: 'none', md: 'flex' }}>
<FiChevronDown />
</Box>
</HStack>
</MenuButton>
<MenuList
bg={useColorModeValue('white', 'gray.900')}
borderColor={useColorModeValue('gray.200', 'gray.700')}
>
<MenuItem onClick={handleLogout}>Sign out</MenuItem>
</MenuList>
</Menu>
</Flex>
</HStack>
</Flex>
);
};

View File

@@ -0,0 +1,565 @@
import React, { useEffect, useRef, useState } from 'react';
import {
Button,
Center,
Flex,
Input,
InputGroup,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Select,
Text,
useDisclosure,
useToast,
Alert,
AlertIcon,
Collapse,
Box,
TableContainer,
Table,
Thead,
Tr,
Th,
Tbody,
Td,
Code,
Radio,
RadioGroup,
Stack,
Textarea,
} from '@chakra-ui/react';
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
import { useClient } from 'urql';
import EmailEditor from 'react-email-editor';
import {
UpdateModalViews,
EmailTemplateInputDataFields,
emailTemplateEventNames,
emailTemplateVariables,
EmailTemplateEditors,
} from '../constants';
import { capitalizeFirstLetter } from '../utils';
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
interface selectedEmailTemplateDataTypes {
[EmailTemplateInputDataFields.ID]: string;
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.CREATED_AT]: number;
[EmailTemplateInputDataFields.TEMPLATE]: string;
[EmailTemplateInputDataFields.DESIGN]: string;
}
interface UpdateEmailTemplateInputPropTypes {
view: UpdateModalViews;
selectedTemplate?: selectedEmailTemplateDataTypes;
fetchEmailTemplatesData: Function;
}
interface templateVariableDataTypes {
text: string;
value: string;
description: string;
}
interface emailTemplateDataType {
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.TEMPLATE]: string;
[EmailTemplateInputDataFields.DESIGN]: string;
}
interface validatorDataType {
[EmailTemplateInputDataFields.SUBJECT]: boolean;
}
const initTemplateData: emailTemplateDataType = {
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
[EmailTemplateInputDataFields.SUBJECT]: '',
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
};
const initTemplateValidatorData: validatorDataType = {
[EmailTemplateInputDataFields.SUBJECT]: true,
};
const UpdateEmailTemplate = ({
view,
selectedTemplate,
fetchEmailTemplatesData,
}: UpdateEmailTemplateInputPropTypes) => {
const client = useClient();
const toast = useToast();
const emailEditorRef = useRef(null);
const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false);
const [editor, setEditor] = useState<string>(
EmailTemplateEditors.PLAIN_HTML_EDITOR,
);
const [templateVariables, setTemplateVariables] = useState<
templateVariableDataTypes[]
>([]);
const [templateData, setTemplateData] = useState<emailTemplateDataType>({
...initTemplateData,
});
const [validator, setValidator] = useState<validatorDataType>({
...initTemplateValidatorData,
});
const [isDynamicVariableInfoOpen, setIsDynamicVariableInfoOpen] =
useState<boolean>(false);
const onReady = () => {
if (selectedTemplate) {
const { design } = selectedTemplate;
try {
if (design) {
const designData = JSON.parse(design);
// @ts-ignore
emailEditorRef.current.editor.loadDesign(designData);
}
} catch (error) {
console.error(error);
onClose();
}
}
};
const inputChangehandler = (inputType: string, value: any) => {
if (inputType !== EmailTemplateInputDataFields.EVENT_NAME) {
setValidator({
...validator,
[inputType]: value?.trim().length,
});
}
setTemplateData({ ...templateData, [inputType]: value });
};
const validateData = () => {
return (
!loading &&
templateData[EmailTemplateInputDataFields.EVENT_NAME].length > 0 &&
templateData[EmailTemplateInputDataFields.SUBJECT].length > 0 &&
validator[EmailTemplateInputDataFields.SUBJECT]
);
};
const updateTemplate = async (params: emailTemplateDataType) => {
let res: any = {};
if (
view === UpdateModalViews.Edit &&
selectedTemplate?.[EmailTemplateInputDataFields.ID]
) {
res = await client
.mutation(EditEmailTemplate, {
params: {
...params,
id: selectedTemplate[EmailTemplateInputDataFields.ID],
},
})
.toPromise();
} else {
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
}
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (
res.data?._add_email_template ||
res.data?._update_email_template
) {
toast({
title: capitalizeFirstLetter(
res.data?._add_email_template?.message ||
res.data?._update_email_template?.message,
),
isClosable: true,
status: 'success',
position: 'top-right',
});
setTemplateData({
...initTemplateData,
});
setValidator({ ...initTemplateValidatorData });
fetchEmailTemplatesData();
}
};
const saveData = async () => {
if (!validateData()) return;
setLoading(true);
let params: emailTemplateDataType = {
[EmailTemplateInputDataFields.EVENT_NAME]:
templateData[EmailTemplateInputDataFields.EVENT_NAME],
[EmailTemplateInputDataFields.SUBJECT]:
templateData[EmailTemplateInputDataFields.SUBJECT],
[EmailTemplateInputDataFields.TEMPLATE]:
templateData[EmailTemplateInputDataFields.TEMPLATE],
[EmailTemplateInputDataFields.DESIGN]: '',
};
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
// @ts-ignore
await emailEditorRef.current.editor.exportHtml(async (data) => {
const { design, html } = data;
if (!html || !design) {
setLoading(false);
return;
}
params = {
...params,
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
};
await updateTemplate(params);
});
} else {
await updateTemplate(params);
}
view === UpdateModalViews.ADD && onClose();
};
const resetData = () => {
if (selectedTemplate) {
setTemplateData(selectedTemplate);
} else {
setTemplateData({ ...initTemplateData });
}
};
// set template data if edit modal is open
useEffect(() => {
if (
isOpen &&
view === UpdateModalViews.Edit &&
selectedTemplate &&
Object.keys(selectedTemplate || {}).length
) {
const { id, created_at, ...rest } = selectedTemplate;
setTemplateData(rest);
}
}, [isOpen]);
// set template variables
useEffect(() => {
const updatedTemplateVariables = Object.entries(
emailTemplateVariables,
).reduce((acc, [key, val]): any => {
if (
(templateData[EmailTemplateInputDataFields.EVENT_NAME] !==
emailTemplateEventNames['Verify Otp'] &&
val === emailTemplateVariables.otp) ||
(templateData[EmailTemplateInputDataFields.EVENT_NAME] ===
emailTemplateEventNames['Verify Otp'] &&
val === emailTemplateVariables.verification_url)
) {
return acc;
}
return [
...acc,
{
text: key,
value: val.value,
description: val.description,
},
];
}, []);
setTemplateVariables(updatedTemplateVariables);
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
// change editor
useEffect(() => {
if (isOpen && selectedTemplate) {
const { design } = selectedTemplate;
if (design) {
setEditor(EmailTemplateEditors.UNLAYER_EDITOR);
} else {
setEditor(EmailTemplateEditors.PLAIN_HTML_EDITOR);
}
}
}, [isOpen, selectedTemplate]);
// reset fields when editor is changed
useEffect(() => {
if (selectedTemplate?.design) {
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate.template,
[EmailTemplateInputDataFields.DESIGN]: selectedTemplate.design,
});
} else {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
});
}
} else if (selectedTemplate?.template) {
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
});
} else {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate?.template,
[EmailTemplateInputDataFields.DESIGN]: '',
});
}
}
}, [editor]);
return (
<>
{view === UpdateModalViews.ADD ? (
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
variant="solid"
onClick={onOpen}
isDisabled={false}
size="sm"
>
<Center h="100%">Add Template</Center>{' '}
</Button>
) : (
<MenuItem onClick={onOpen}>Edit</MenuItem>
)}
<Modal
isOpen={isOpen}
onClose={() => {
resetData();
onClose();
}}
size="6xl"
>
<ModalOverlay />
<ModalContent>
<ModalHeader>
{view === UpdateModalViews.ADD
? 'Add New Email Template'
: 'Edit Email Template'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex
flexDirection="column"
border="1px"
borderRadius="md"
borderColor="gray.200"
p="5"
>
<Alert
status="info"
onClick={() =>
setIsDynamicVariableInfoOpen(!isDynamicVariableInfoOpen)
}
borderRadius="5"
marginBottom={5}
cursor="pointer"
fontSize="sm"
>
<AlertIcon />
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
>
<Box width="85%">
<b>Note:</b> You can add set of dynamic variables to subject
and email body. Click here to see the set of dynamic
variables.
</Box>
{isDynamicVariableInfoOpen ? <FaAngleUp /> : <FaAngleDown />}
</Flex>
</Alert>
<Collapse
style={{
width: '100%',
}}
in={isDynamicVariableInfoOpen}
>
<TableContainer
background="gray.100"
borderRadius={5}
height={200}
width="100%"
overflowY="auto"
overflowWrap="break-word"
>
<Table variant="simple">
<Thead>
<Tr>
<Th>Variable</Th>
<Th>Description</Th>
</Tr>
</Thead>
<Tbody>
{templateVariables.map((i) => (
<Tr key={i.text}>
<Td>
<Code fontSize="sm">{`{{.${i.text}}}`}</Code>
</Td>
<Td>
<Text
size="sm"
fontSize="sm"
overflowWrap="break-word"
width="100%"
>
{i.description}
</Text>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Collapse>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Event Name</Flex>
<Flex flex="3">
<Select
size="md"
value={
templateData[EmailTemplateInputDataFields.EVENT_NAME]
}
onChange={(e) =>
inputChangehandler(
EmailTemplateInputDataFields.EVENT_NAME,
e.currentTarget.value,
)
}
>
{Object.entries(emailTemplateEventNames).map(
([key, value]: any) => (
<option value={value} key={key}>
{key}
</option>
),
)}
</Select>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Subject</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="Subject Line"
value={templateData[EmailTemplateInputDataFields.SUBJECT]}
isInvalid={
!validator[EmailTemplateInputDataFields.SUBJECT]
}
onChange={(e) =>
inputChangehandler(
EmailTemplateInputDataFields.SUBJECT,
e.currentTarget.value,
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="flex-start"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Template Body</Flex>
<Flex flex="3">
<RadioGroup
onChange={(value) => setEditor(value)}
value={editor}
>
<Stack direction="row" spacing="50px">
<Radio value={EmailTemplateEditors.PLAIN_HTML_EDITOR}>
Plain HTML
</Radio>
<Radio value={EmailTemplateEditors.UNLAYER_EDITOR}>
Unlayer Editor
</Radio>
</Stack>
</RadioGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="flex-start"
alignItems="center"
border="1px solid"
borderColor="gray.200"
>
{editor === EmailTemplateEditors.UNLAYER_EDITOR ? (
<EmailEditor ref={emailEditorRef} onReady={onReady} />
) : (
<Textarea
value={templateData.template}
onChange={(e) => {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: e.target.value,
});
}}
placeholder="Template HTML"
border="0"
height="500px"
/>
)}
</Flex>
</Flex>
</ModalBody>
<ModalFooter>
<Button
variant="outline"
onClick={resetData}
isDisabled={loading}
marginRight="5"
>
Reset
</Button>
<Button
colorScheme="blue"
variant="solid"
isLoading={loading}
onClick={saveData}
isDisabled={!validateData()}
>
<Center h="100%" pt="5%">
Save
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default UpdateEmailTemplate;

View File

@@ -0,0 +1,697 @@
import React, { useEffect, useState } from 'react';
import {
Button,
Center,
Code,
Collapse,
Flex,
Input,
InputGroup,
InputRightElement,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Select,
Switch,
Text,
useDisclosure,
useToast,
Alert,
AlertIcon,
Divider,
} from '@chakra-ui/react';
import {
FaAngleDown,
FaAngleUp,
FaMinusCircle,
FaPlus,
FaRegClone,
} from 'react-icons/fa';
import { useClient } from 'urql';
import {
webhookEventNames,
ArrayInputOperations,
WebhookInputDataFields,
WebhookInputHeaderFields,
UpdateModalViews,
webhookVerifiedStatus,
webhookPayloadExample,
} from '../constants';
import {
capitalizeFirstLetter,
copyTextToClipboard,
validateURI,
} from '../utils';
import { AddWebhook, EditWebhook, TestEndpoint } from '../graphql/mutation';
import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi';
interface headersDataType {
[WebhookInputHeaderFields.KEY]: string;
[WebhookInputHeaderFields.VALUE]: string;
}
interface headersValidatorDataType {
[WebhookInputHeaderFields.KEY]: boolean;
[WebhookInputHeaderFields.VALUE]: boolean;
}
interface selecetdWebhookDataTypes {
[WebhookInputDataFields.ID]: string;
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
}
interface UpdateWebhookModalInputPropTypes {
view: UpdateModalViews;
selectedWebhook?: selecetdWebhookDataTypes;
fetchWebookData: Function;
}
const initHeadersData: headersDataType = {
[WebhookInputHeaderFields.KEY]: '',
[WebhookInputHeaderFields.VALUE]: '',
};
const initHeadersValidatorData: headersValidatorDataType = {
[WebhookInputHeaderFields.KEY]: true,
[WebhookInputHeaderFields.VALUE]: true,
};
interface webhookDataType {
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]: headersDataType[];
}
interface validatorDataType {
[WebhookInputDataFields.ENDPOINT]: boolean;
[WebhookInputDataFields.HEADERS]: headersValidatorDataType[];
}
const initWebhookData: webhookDataType = {
[WebhookInputDataFields.EVENT_NAME]: webhookEventNames['User login'],
[WebhookInputDataFields.EVENT_DESCRIPTION]: '',
[WebhookInputDataFields.ENDPOINT]: '',
[WebhookInputDataFields.ENABLED]: true,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
};
const initWebhookValidatorData: validatorDataType = {
[WebhookInputDataFields.ENDPOINT]: true,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersValidatorData }],
};
const UpdateWebhookModal = ({
view,
selectedWebhook,
fetchWebookData,
}: UpdateWebhookModalInputPropTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false);
const [verifyingEndpoint, setVerifyingEndpoint] = useState<boolean>(false);
const [isShowingPayload, setIsShowingPayload] = useState<boolean>(false);
const [webhook, setWebhook] = useState<webhookDataType>({
...initWebhookData,
});
const [validator, setValidator] = useState<validatorDataType>({
...initWebhookValidatorData,
});
const [verifiedStatus, setVerifiedStatus] = useState<webhookVerifiedStatus>(
webhookVerifiedStatus.PENDING,
);
const inputChangehandler = (
inputType: string,
value: any,
headerInputType: string = WebhookInputHeaderFields.KEY,
headerIndex: number = 0,
) => {
if (
verifiedStatus !== webhookVerifiedStatus.PENDING &&
inputType !== WebhookInputDataFields.ENABLED
) {
setVerifiedStatus(webhookVerifiedStatus.PENDING);
}
switch (inputType) {
case WebhookInputDataFields.EVENT_NAME:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.EVENT_DESCRIPTION:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.ENDPOINT:
setWebhook({ ...webhook, [inputType]: value });
setValidator({
...validator,
[WebhookInputDataFields.ENDPOINT]: validateURI(value),
});
break;
case WebhookInputDataFields.ENABLED:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.HEADERS:
const updatedHeaders: any = [
...webhook[WebhookInputDataFields.HEADERS],
];
const updatedHeadersValidatorData: any = [
...validator[WebhookInputDataFields.HEADERS],
];
const otherHeaderInputType =
headerInputType === WebhookInputHeaderFields.KEY
? WebhookInputHeaderFields.VALUE
: WebhookInputHeaderFields.KEY;
updatedHeaders[headerIndex][headerInputType] = value;
updatedHeadersValidatorData[headerIndex][headerInputType] =
value.length > 0
? updatedHeaders[headerIndex][otherHeaderInputType].length > 0
: updatedHeaders[headerIndex][otherHeaderInputType].length === 0;
updatedHeadersValidatorData[headerIndex][otherHeaderInputType] =
value.length > 0
? updatedHeaders[headerIndex][otherHeaderInputType].length > 0
: updatedHeaders[headerIndex][otherHeaderInputType].length === 0;
setWebhook({ ...webhook, [inputType]: updatedHeaders });
setValidator({
...validator,
[inputType]: updatedHeadersValidatorData,
});
break;
default:
break;
}
};
const updateHeaders = (operation: string, index: number = 0) => {
if (verifiedStatus !== webhookVerifiedStatus.PENDING) {
setVerifiedStatus(webhookVerifiedStatus.PENDING);
}
switch (operation) {
case ArrayInputOperations.APPEND:
setWebhook({
...webhook,
[WebhookInputDataFields.HEADERS]: [
...(webhook?.[WebhookInputDataFields.HEADERS] || []),
{ ...initHeadersData },
],
});
setValidator({
...validator,
[WebhookInputDataFields.HEADERS]: [
...(validator?.[WebhookInputDataFields.HEADERS] || []),
{ ...initHeadersValidatorData },
],
});
break;
case ArrayInputOperations.REMOVE:
if (webhook?.[WebhookInputDataFields.HEADERS]?.length) {
const updatedHeaders = [...webhook[WebhookInputDataFields.HEADERS]];
updatedHeaders.splice(index, 1);
setWebhook({
...webhook,
[WebhookInputDataFields.HEADERS]: updatedHeaders,
});
}
if (validator?.[WebhookInputDataFields.HEADERS]?.length) {
const updatedHeadersData = [
...validator[WebhookInputDataFields.HEADERS],
];
updatedHeadersData.splice(index, 1);
setValidator({
...validator,
[WebhookInputDataFields.HEADERS]: updatedHeadersData,
});
}
break;
default:
break;
}
};
const validateData = () => {
return (
!loading &&
!verifyingEndpoint &&
webhook[WebhookInputDataFields.EVENT_NAME].length > 0 &&
webhook[WebhookInputDataFields.ENDPOINT].length > 0 &&
validator[WebhookInputDataFields.ENDPOINT] &&
!validator[WebhookInputDataFields.HEADERS].some(
(headerData: headersValidatorDataType) =>
!headerData.key || !headerData.value,
)
);
};
const getParams = () => {
let params: any = {
[WebhookInputDataFields.EVENT_NAME]:
webhook[WebhookInputDataFields.EVENT_NAME],
[WebhookInputDataFields.EVENT_DESCRIPTION]:
webhook[WebhookInputDataFields.EVENT_DESCRIPTION],
[WebhookInputDataFields.ENDPOINT]:
webhook[WebhookInputDataFields.ENDPOINT],
[WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED],
[WebhookInputDataFields.HEADERS]: {},
};
if (webhook[WebhookInputDataFields.HEADERS].length) {
const headers = webhook[WebhookInputDataFields.HEADERS].reduce(
(acc, data) => {
return data.key ? { ...acc, [data.key]: data.value } : acc;
},
{},
);
if (Object.keys(headers).length) {
params[WebhookInputDataFields.HEADERS] = headers;
}
}
return params;
};
const saveData = async () => {
if (!validateData()) return;
setLoading(true);
const params = getParams();
let res: any = {};
if (
view === UpdateModalViews.Edit &&
selectedWebhook?.[WebhookInputDataFields.ID]
) {
res = await client
.mutation(EditWebhook, {
params: {
...params,
id: selectedWebhook[WebhookInputDataFields.ID],
},
})
.toPromise();
} else {
res = await client.mutation(AddWebhook, { params }).toPromise();
}
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (res.data?._add_webhook || res.data?._update_webhook) {
toast({
title: capitalizeFirstLetter(
res.data?._add_webhook?.message || res.data?._update_webhook?.message,
),
isClosable: true,
status: 'success',
position: 'top-right',
});
setWebhook({
...initWebhookData,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
});
setValidator({ ...initWebhookValidatorData });
fetchWebookData();
}
view === UpdateModalViews.ADD && onClose();
};
useEffect(() => {
if (
isOpen &&
view === UpdateModalViews.Edit &&
selectedWebhook &&
Object.keys(selectedWebhook || {}).length
) {
const { headers, ...rest } = selectedWebhook;
const headerItems = Object.entries(headers || {});
if (headerItems.length) {
let formattedHeadersData = headerItems.map((headerData) => {
return {
[WebhookInputHeaderFields.KEY]: headerData[0],
[WebhookInputHeaderFields.VALUE]: headerData[1],
};
});
setWebhook({
...rest,
[WebhookInputDataFields.HEADERS]: formattedHeadersData,
});
setValidator({
...validator,
[WebhookInputDataFields.HEADERS]: new Array(
formattedHeadersData.length,
)
.fill({})
.map(() => ({ ...initHeadersValidatorData })),
});
} else {
setWebhook({
...rest,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
});
}
}
}, [isOpen]);
const verifyEndpoint = async () => {
if (!validateData()) return;
setVerifyingEndpoint(true);
const { [WebhookInputDataFields.ENABLED]: _, ...params } = getParams();
const res = await client.mutation(TestEndpoint, { params }).toPromise();
if (
res.data?._test_endpoint?.http_status >= 200 &&
res.data?._test_endpoint?.http_status < 400
) {
setVerifiedStatus(webhookVerifiedStatus.VERIFIED);
} else {
setVerifiedStatus(webhookVerifiedStatus.NOT_VERIFIED);
}
setVerifyingEndpoint(false);
};
return (
<>
{view === UpdateModalViews.ADD ? (
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
variant="solid"
onClick={onOpen}
isDisabled={false}
size="sm"
>
<Center h="100%">Add Webhook</Center>{' '}
</Button>
) : (
<MenuItem onClick={onOpen}>Edit</MenuItem>
)}
<Modal isOpen={isOpen} onClose={onClose} size="3xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>
{view === UpdateModalViews.ADD ? 'Add New Webhook' : 'Edit Webhook'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex
flexDirection="column"
border="1px"
borderRadius="md"
borderColor="gray.200"
p="5"
>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Event Name</Flex>
<Flex flex="3">
<Select
size="md"
value={
webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]
}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.EVENT_NAME,
e.currentTarget.value,
)
}
>
{Object.entries(webhookEventNames).map(
([key, value]: any) => (
<option value={value} key={key}>
{key}
</option>
),
)}
</Select>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Event Description</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="User event"
value={webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.EVENT_DESCRIPTION,
e.currentTarget.value,
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Endpoint</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="https://domain.com/webhook"
value={webhook[WebhookInputDataFields.ENDPOINT]}
isInvalid={!validator[WebhookInputDataFields.ENDPOINT]}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.ENDPOINT,
e.currentTarget.value,
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Enabled</Flex>
<Flex w="25%" justifyContent="space-between">
<Text h="75%" fontWeight="bold" marginRight="2">
Off
</Text>
<Switch
size="md"
isChecked={webhook[WebhookInputDataFields.ENABLED]}
onChange={() =>
inputChangehandler(
WebhookInputDataFields.ENABLED,
!webhook[WebhookInputDataFields.ENABLED],
)
}
/>
<Text h="75%" fontWeight="bold" marginLeft="2">
On
</Text>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="5%"
>
<Flex>Headers</Flex>
<Flex>
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
h="1.75rem"
size="sm"
variant="ghost"
paddingRight="0"
onClick={() => updateHeaders(ArrayInputOperations.APPEND)}
>
Add more Headers
</Button>
</Flex>
</Flex>
<Flex flexDirection="column" maxH={220} overflowY="auto">
{webhook[WebhookInputDataFields.HEADERS]?.map(
(headerData, index) => (
<Flex
key={`header-data-${index}`}
justifyContent="center"
alignItems="center"
>
<InputGroup size="md" marginBottom="2.5%">
<Input
type="text"
placeholder="key"
value={headerData[WebhookInputHeaderFields.KEY]}
isInvalid={
!validator[WebhookInputDataFields.HEADERS][index]?.[
WebhookInputHeaderFields.KEY
]
}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.HEADERS,
e.target.value,
WebhookInputHeaderFields.KEY,
index,
)
}
width="30%"
marginRight="2%"
/>
<Center marginRight="2%">
<Text fontWeight="bold">:</Text>
</Center>
<Input
type="text"
placeholder="value"
value={headerData[WebhookInputHeaderFields.VALUE]}
isInvalid={
!validator[WebhookInputDataFields.HEADERS][index]?.[
WebhookInputHeaderFields.VALUE
]
}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.HEADERS,
e.target.value,
WebhookInputHeaderFields.VALUE,
index,
)
}
width="65%"
/>
<InputRightElement width="3rem">
<Button
width="6rem"
colorScheme="blackAlpha"
variant="ghost"
padding="0"
onClick={() =>
updateHeaders(ArrayInputOperations.REMOVE, index)
}
>
<FaMinusCircle />
</Button>
</InputRightElement>
</InputGroup>
</Flex>
),
)}
</Flex>
<Divider marginY={5} />
<Alert
status="info"
onClick={() => setIsShowingPayload(!isShowingPayload)}
borderRadius="5"
cursor="pointer"
fontSize="sm"
>
<AlertIcon />
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
>
Checkout the example payload
{isShowingPayload ? <FaAngleUp /> : <FaAngleDown />}
</Flex>
</Alert>
<Collapse
style={{
marginTop: 10,
width: '100%',
}}
in={isShowingPayload}
>
<Code
width="inherit"
borderRadius={5}
padding={2}
position="relative"
>
<pre style={{ overflow: 'auto' }}>
{webhookPayloadExample}
</pre>
{isShowingPayload && (
<Flex
position="absolute"
top={4}
right={4}
cursor="pointer"
onClick={() => copyTextToClipboard(webhookPayloadExample)}
>
<FaRegClone color="#bfbfbf" />
</Flex>
)}
</Code>
</Collapse>
</Flex>
</ModalBody>
<ModalFooter>
<Button
colorScheme={
verifiedStatus === webhookVerifiedStatus.VERIFIED
? 'green'
: verifiedStatus === webhookVerifiedStatus.PENDING
? 'yellow'
: 'red'
}
variant="outline"
onClick={verifyEndpoint}
isLoading={verifyingEndpoint}
isDisabled={!validateData()}
marginRight="5"
leftIcon={
verifiedStatus === webhookVerifiedStatus.VERIFIED ? (
<BiCheckCircle />
) : verifiedStatus === webhookVerifiedStatus.PENDING ? (
<BiErrorCircle />
) : (
<BiError />
)
}
>
{verifiedStatus === webhookVerifiedStatus.VERIFIED
? 'Endpoint Verified'
: verifiedStatus === webhookVerifiedStatus.PENDING
? 'Test Endpoint'
: 'Endpoint Not Verified'}
</Button>
<Button
colorScheme="blue"
variant="solid"
onClick={saveData}
isDisabled={!validateData()}
>
<Center h="100%" pt="5%">
Save
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default UpdateWebhookModal;

View File

@@ -0,0 +1,426 @@
import React, { useEffect, useState } from 'react';
import dayjs from 'dayjs';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
Spinner,
Table,
Th,
Thead,
Tr,
Tbody,
IconButton,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
TableCaption,
Tooltip,
Td,
Tag,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import {
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaAngleLeft,
FaAngleRight,
FaExclamationCircle,
FaRegClone,
} from 'react-icons/fa';
import { copyTextToClipboard } from '../utils';
import { WebhookLogsQuery } from '../graphql/queries';
import { pageLimits } from '../constants';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface deleteWebhookModalInputPropTypes {
webhookId: string;
eventName: string;
}
interface webhookLogsDataTypes {
id: string;
http_status: number;
request: string;
response: string;
created_at: number;
}
const ViewWebhookLogsModal = ({
webhookId,
eventName,
}: deleteWebhookModalInputPropTypes) => {
const client = useClient();
const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false);
const [webhookLogs, setWebhookLogs] = useState<webhookLogsDataTypes[]>([]);
const [paginationProps, setPaginationProps] = useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const fetchWebhookLogsData = async () => {
setLoading(true);
const res = await client
.query(WebhookLogsQuery, {
params: {
webhook_id: webhookId,
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (res.data?._webhook_logs) {
const { pagination, webhook_logs } = res.data?._webhook_logs;
const maxPages = getMaxPages(pagination);
if (webhook_logs?.length) {
setWebhookLogs(webhook_logs);
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
useEffect(() => {
isOpen && fetchWebhookLogsData();
}, [isOpen, paginationProps.page, paginationProps.limit]);
return (
<>
<MenuItem onClick={onOpen}>View Logs</MenuItem>
<Modal isOpen={isOpen} onClose={onClose} size="4xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>Webhook Logs - {eventName}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex
flexDirection="column"
border="1px"
borderRadius="md"
borderColor="gray.200"
p="5"
>
{!loading ? (
webhookLogs.length ? (
<Table variant="simple">
<Thead>
<Tr>
<Th>ID</Th>
<Th>Created At</Th>
<Th>Http Status</Th>
<Th>Request</Th>
<Th>Response</Th>
</Tr>
</Thead>
<Tbody>
{webhookLogs.map((logData: webhookLogsDataTypes) => (
<Tr key={logData.id} style={{ fontSize: 14 }}>
<Td>
<Text fontSize="sm">{`${logData.id.substring(
0,
5,
)}***${logData.id.substring(
logData.id.length - 5,
logData.id.length,
)}`}</Text>
</Td>
<Td>
{dayjs(logData.created_at * 1000).format(
'MMM DD, YYYY',
)}
</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
logData.http_status >= 400 ? 'red' : 'green'
}
>
{logData.http_status}
</Tag>
</Td>
<Td>
<Flex alignItems="center">
<Tooltip
bg="gray.300"
color="black"
label={logData.request || 'null'}
>
<Tag
size="sm"
variant="outline"
colorScheme={
logData.request ? 'gray' : 'yellow'
}
>
{logData.request ? 'Payload' : 'No Data'}
</Tag>
</Tooltip>
{logData.request && (
<Button
size="xs"
variant="outline"
marginLeft="5px"
h="21px"
onClick={() =>
copyTextToClipboard(logData.request)
}
>
<FaRegClone color="#bfbfbf" />
</Button>
)}
</Flex>
</Td>
<Td>
<Flex alignItems="center">
<Tooltip
bg="gray.300"
color="black"
label={logData.response || 'null'}
>
<Tag
size="sm"
variant="outline"
colorScheme={
logData.response ? 'gray' : 'yellow'
}
>
{logData.response ? 'Preview' : 'No Data'}
</Tag>
</Tooltip>
{logData.response && (
<Button
size="xs"
variant="outline"
marginLeft="5px"
h="21px"
onClick={() =>
copyTextToClipboard(logData.response)
}
>
<FaRegClone color="#bfbfbf" />
</Button>
)}
</Flex>
</Td>
</Tr>
))}
</Tbody>
{(paginationProps.maxPages > 1 ||
paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{pageLimits.map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >=
paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >=
paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle
style={{ color: '#f0f0f0', fontSize: 70 }}
/>
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Flex>
</ModalBody>
<ModalFooter>
<Button
colorScheme="blue"
variant="solid"
onClick={onClose}
isDisabled={false}
>
<Center h="100%" pt="5%">
Close
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default ViewWebhookLogsModal;

391
dashboard/src/constants.ts Normal file
View File

@@ -0,0 +1,391 @@
export const LOGO_URL =
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
export const TextInputType = {
ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME',
CLIENT_ID: 'CLIENT_ID',
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
DISCORD_CLIENT_ID: 'DISCORD_CLIENT_ID',
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID',
TWITCH_CLIENT_ID: 'TWITCH_CLIENT_ID',
ROBLOX_CLIENT_ID: 'ROBLOX_CLIENT_ID',
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
REDIS_URL: 'REDIS_URL',
SMTP_HOST: 'SMTP_HOST',
SMTP_PORT: 'SMTP_PORT',
SMTP_USERNAME: 'SMTP_USERNAME',
SMTP_LOCAL_NAME: 'SMTP_LOCAL_NAME',
SENDER_EMAIL: 'SENDER_EMAIL',
SENDER_NAME: 'SENDER_NAME',
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
DATABASE_NAME: 'DATABASE_NAME',
DATABASE_TYPE: 'DATABASE_TYPE',
DATABASE_URL: 'DATABASE_URL',
GIVEN_NAME: 'given_name',
MIDDLE_NAME: 'middle_name',
FAMILY_NAME: 'family_name',
NICKNAME: 'nickname',
PHONE_NUMBER: 'phone_number',
PICTURE: 'picture',
};
export const HiddenInputType = {
CLIENT_SECRET: 'CLIENT_SECRET',
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
DISCORD_CLIENT_SECRET: 'DISCORD_CLIENT_SECRET',
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET',
TWITCH_CLIENT_SECRET: 'TWITCH_CLIENT_SECRET',
ROBLOX_CLIENT_SECRET: 'ROBLOX_CLIENT_SECRET',
JWT_SECRET: 'JWT_SECRET',
SMTP_PASSWORD: 'SMTP_PASSWORD',
ADMIN_SECRET: 'ADMIN_SECRET',
OLD_ADMIN_SECRET: 'OLD_ADMIN_SECRET',
};
export const ArrayInputType = {
ROLES: 'ROLES',
DEFAULT_ROLES: 'DEFAULT_ROLES',
PROTECTED_ROLES: 'PROTECTED_ROLES',
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
};
export const SelectInputType = {
JWT_TYPE: 'JWT_TYPE',
GENDER: 'gender',
DEFAULT_AUTHORIZE_RESPONSE_TYPE: 'DEFAULT_AUTHORIZE_RESPONSE_TYPE',
DEFAULT_AUTHORIZE_RESPONSE_MODE: 'DEFAULT_AUTHORIZE_RESPONSE_MODE',
};
export const MultiSelectInputType = {
USER_ROLES: 'roles',
};
export const TextAreaInputType = {
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
JWT_PUBLIC_KEY: 'JWT_PUBLIC_KEY',
};
export const SwitchInputType = {
APP_COOKIE_SECURE: 'APP_COOKIE_SECURE',
ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE',
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
DISABLE_MOBILE_BASIC_AUTHENTICATION: 'DISABLE_MOBILE_BASIC_AUTHENTICATION',
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
DISABLE_PLAYGROUND: 'DISABLE_PLAYGROUND',
DISABLE_TOTP_LOGIN: 'DISABLE_TOTP_LOGIN',
DISABLE_MAIL_OTP_LOGIN: 'DISABLE_MAIL_OTP_LOGIN',
};
export const DateInputType = {
BIRTHDATE: 'birthdate',
};
export const ArrayInputOperations = {
APPEND: 'APPEND',
REMOVE: 'REMOVE',
};
export const HMACEncryptionType = {
HS256: 'HS256',
HS384: 'HS384',
HS512: 'HS512',
};
export const RSAEncryptionType = {
RS256: 'RS256',
RS384: 'RS384',
RS512: 'RS512',
};
export const ECDSAEncryptionType = {
ES256: 'ES256',
ES384: 'ES384',
ES512: 'ES512',
};
export interface envVarTypes {
GOOGLE_CLIENT_ID: string;
GOOGLE_CLIENT_SECRET: string;
GITHUB_CLIENT_ID: string;
GITHUB_CLIENT_SECRET: string;
FACEBOOK_CLIENT_ID: string;
FACEBOOK_CLIENT_SECRET: string;
LINKEDIN_CLIENT_ID: string;
LINKEDIN_CLIENT_SECRET: string;
APPLE_CLIENT_ID: string;
APPLE_CLIENT_SECRET: string;
DISCORD_CLIENT_ID: string;
DISCORD_CLIENT_SECRET: string;
TWITTER_CLIENT_ID: string;
TWITTER_CLIENT_SECRET: string;
MICROSOFT_CLIENT_ID: string;
MICROSOFT_CLIENT_SECRET: string;
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: string;
TWITCH_CLIENT_ID: string;
TWITCH_CLIENT_SECRET: string;
ROBLOX_CLIENT_ID: string;
ROBLOX_CLIENT_SECRET: string;
ROLES: [string] | [];
DEFAULT_ROLES: [string] | [];
PROTECTED_ROLES: [string] | [];
JWT_TYPE: string;
JWT_SECRET: string;
JWT_ROLE_CLAIM: string;
JWT_PRIVATE_KEY: string;
JWT_PUBLIC_KEY: string;
REDIS_URL: string;
SMTP_HOST: string;
SMTP_PORT: string;
SMTP_USERNAME: string;
SMTP_PASSWORD: string;
SMTP_LOCAL_NAME: string;
SENDER_EMAIL: string;
SENDER_NAME: string;
ALLOWED_ORIGINS: [string] | [];
ORGANIZATION_NAME: string;
ORGANIZATION_LOGO: string;
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
ADMIN_SECRET: string;
APP_COOKIE_SECURE: boolean;
ADMIN_COOKIE_SECURE: boolean;
DISABLE_LOGIN_PAGE: boolean;
DISABLE_MAGIC_LINK_LOGIN: boolean;
DISABLE_EMAIL_VERIFICATION: boolean;
DISABLE_BASIC_AUTHENTICATION: boolean;
DISABLE_MOBILE_BASIC_AUTHENTICATION: boolean;
DISABLE_SIGN_UP: boolean;
DISABLE_STRONG_PASSWORD: boolean;
OLD_ADMIN_SECRET: string;
DATABASE_NAME: string;
DATABASE_TYPE: string;
DATABASE_URL: string;
ACCESS_TOKEN_EXPIRY_TIME: string;
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
DEFAULT_AUTHORIZE_RESPONSE_TYPE: string;
DEFAULT_AUTHORIZE_RESPONSE_MODE: string;
DISABLE_PLAYGROUND: boolean;
DISABLE_TOTP_LOGIN: boolean;
DISABLE_MAIL_OTP_LOGIN: boolean;
}
export const envSubViews = {
INSTANCE_INFO: 'instance-info',
ROLES: 'roles',
JWT_CONFIG: 'jwt-config',
SESSION_STORAGE: 'session-storage',
EMAIL_CONFIG: 'email-config',
WHITELIST_VARIABLES: 'whitelist-variables',
ORGANIZATION_INFO: 'organization-info',
ACCESS_TOKEN: 'access-token',
FEATURES: 'features',
ADMIN_SECRET: 'admin-secret',
DB_CRED: 'db-cred',
};
export enum WebhookInputDataFields {
ID = 'id',
EVENT_DESCRIPTION = 'event_description',
EVENT_NAME = 'event_name',
ENDPOINT = 'endpoint',
ENABLED = 'enabled',
HEADERS = 'headers',
}
export enum EmailTemplateInputDataFields {
ID = 'id',
EVENT_NAME = 'event_name',
SUBJECT = 'subject',
CREATED_AT = 'created_at',
TEMPLATE = 'template',
DESIGN = 'design',
}
export enum WebhookInputHeaderFields {
KEY = 'key',
VALUE = 'value',
}
export enum UpdateModalViews {
ADD = 'add',
Edit = 'edit',
}
export const pageLimits: number[] = [5, 10, 15];
export const webhookEventNames = {
'User signup': 'user.signup',
'User created': 'user.created',
'User login': 'user.login',
'User deleted': 'user.deleted',
'User access enabled': 'user.access_enabled',
'User access revoked': 'user.access_revoked',
'User deactivated': 'user.deactivated',
};
export const emailTemplateEventNames = {
Signup: 'basic_auth_signup',
'Magic Link Login': 'magic_link_login',
'Update Email': 'update_email',
'Forgot Password': 'forgot_password',
'Verify Otp': 'verify_otp',
'Invite member': 'invite_member',
};
export enum webhookVerifiedStatus {
VERIFIED = 'verified',
NOT_VERIFIED = 'not_verified',
PENDING = 'verification_pending',
}
export const emailTemplateVariables = {
'user.id': {
description: `User identifier`,
value: '{.user.id}}',
},
'user.email': {
description: 'User email address',
value: '{.user.email}}',
},
'user.given_name': {
description: `User first name`,
value: '{.user.given_name}}',
},
'user.family_name': {
description: `User last name`,
value: '{.user.family_name}}',
},
'user.middle_name': {
description: `Middle name of user`,
value: '{.user.middle_name}}',
},
'user.nickname': {
description: `Nick name of user`,
value: '{.user.nickname}}',
},
'user.preferred_username': {
description: `Username, by default it is email`,
value: '{.user.preferred_username}}',
},
'user.signup_methods': {
description: `Comma separated list of methods using which user has signed up`,
value: '{.user.signup_methods}}',
},
'user.email_verified': {
description: `Whether email is verified or not`,
value: '{.user.email_verified}}',
},
'user.picture': {
description: `URL of the user profile picture`,
value: '{.user.picture}}',
},
'user.roles': {
description: `Comma separated list of roles assigned to user`,
value: '{.user.roles}}',
},
'user.gender': {
description: `Gender of user`,
value: '{.user.gender}}',
},
'user.birthdate': {
description: `BirthDate of user`,
value: '{.user.birthdate}}',
},
'user.phone_number': {
description: `Phone number of user`,
value: '{.user.phone_number}}',
},
'user.phone_number_verified': {
description: `Whether phone number is verified or not`,
value: '{.user.phone_number_verified}}',
},
'user.created_at': {
description: `User created at time`,
value: '{.user.created_at}}',
},
'user.updated_at': {
description: `Last updated time at user`,
value: '{.user.updated_at}}',
},
'organization.name': {
description: `Organization name`,
value: '{.organization.name}}',
},
'organization.logo': {
description: `Organization logo`,
value: '{.organization.logo}}',
},
verification_url: {
description: `Verification URL in case of events other than verify otp`,
value: '{.verification_url}}',
},
otp: {
description: `OTP sent during login with Multi factor authentication`,
value: '{.otp}}',
},
};
export const webhookPayloadExample: string = `{
"event_name":"user.login",
"user":{
"birthdate":null,
"created_at":1657524721,
"email":"lakhan.m.samani@gmail.com",
"email_verified":true,
"family_name":"Samani",
"gender":null,
"given_name":"Lakhan",
"id":"466d0b31-1b87-420e-bea5-09d05d79c586",
"middle_name":null,
"nickname":null,
"phone_number":null,
"phone_number_verified":false,
"picture":"https://lh3.googleusercontent.com/a-/AFdZucppvU6a2zIDkX0wvhhapVjT0ZMKDlYCkQDi3NxcUg=s96-c",
"preferred_username":"lakhan.m.samani@gmail.com",
"revoked_timestamp":null,
"roles":[
"user"
],
"signup_methods":"google",
"updated_at":1657526492
},
"auth_recipe":"google"
}`;
export enum EmailTemplateEditors {
UNLAYER_EDITOR = 'unlayer_editor',
PLAIN_HTML_EDITOR = 'plain_html_editor',
}
export const ResponseTypes = {
token: 'token',
code: 'code',
id_token: 'id_token',
};
export const ResponseModes = {
query: 'query',
form_post: 'form_post',
fragment: 'fragment',
web_message: 'web_message',
};

View File

@@ -0,0 +1,48 @@
import React, { createContext, useState, useContext, useEffect } from 'react';
import { Center, Spinner } from '@chakra-ui/react';
import { useQuery } from 'urql';
import { useLocation, useNavigate } from 'react-router-dom';
import { AdminSessionQuery } from '../graphql/queries';
import { hasAdminSecret } from '../utils';
const AuthContext = createContext({
isLoggedIn: false,
setIsLoggedIn: (data: boolean) => {},
});
export const AuthContextProvider = ({ children }: { children: any }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const { pathname } = useLocation();
const navigate = useNavigate();
const [{ fetching, data, error }] = useQuery({
query: AdminSessionQuery,
});
useEffect(() => {
if (!fetching && !error) {
setIsLoggedIn(true);
if (pathname === '/login' || pathname === 'signup') {
navigate('/', { replace: true });
}
}
}, [fetching, error]);
if (fetching) {
return (
<Center h="100%">
<Spinner />
</Center>
);
}
return (
<AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
{children}
</AuthContext.Provider>
);
};
export const useAuthContext = () => useContext(AuthContext);

View File

@@ -0,0 +1,138 @@
export const AdminSignup = `
mutation adminSignup($secret: String!) {
_admin_signup (params: {admin_secret: $secret}) {
message
}
}
`;
export const AdminLogin = `
mutation adminLogin($secret: String!){
_admin_login(params: { admin_secret: $secret }) {
message
}
}
`;
export const AdminLogout = `
mutation adminLogout {
_admin_logout {
message
}
}
`;
export const UpdateEnvVariables = `
mutation updateEnvVariables($params: UpdateEnvInput!) {
_update_env(params: $params) {
message
}
}
`;
export const UpdateUser = `
mutation updateUser($params: UpdateUserInput!) {
_update_user(params: $params) {
id
}
}
`;
export const DeleteUser = `
mutation deleteUser($params: DeleteUserInput!) {
_delete_user(params: $params) {
message
}
}
`;
export const InviteMembers = `
mutation inviteMembers($params: InviteMemberInput!) {
_invite_members(params: $params) {
message
}
}
`;
export const RevokeAccess = `
mutation revokeAccess($param: UpdateAccessInput!) {
_revoke_access(param: $param) {
message
}
}
`;
export const EnableAccess = `
mutation revokeAccess($param: UpdateAccessInput!) {
_enable_access(param: $param) {
message
}
}
`;
export const GenerateKeys = `
mutation generateKeys($params: GenerateJWTKeysInput!) {
_generate_jwt_keys(params: $params) {
secret
public_key
private_key
}
}
`;
export const AddWebhook = `
mutation addWebhook($params: AddWebhookRequest!) {
_add_webhook(params: $params) {
message
}
}
`;
export const EditWebhook = `
mutation editWebhook($params: UpdateWebhookRequest!) {
_update_webhook(params: $params) {
message
}
}
`;
export const DeleteWebhook = `
mutation deleteWebhook($params: WebhookRequest!) {
_delete_webhook(params: $params) {
message
}
}
`;
export const TestEndpoint = `
mutation testEndpoint($params: TestEndpointRequest!) {
_test_endpoint(params: $params) {
http_status
response
}
}
`;
export const AddEmailTemplate = `
mutation addEmailTemplate($params: AddEmailTemplateRequest!) {
_add_email_template(params: $params) {
message
}
}
`;
export const EditEmailTemplate = `
mutation editEmailTemplate($params: UpdateEmailTemplateRequest!) {
_update_email_template(params: $params) {
message
}
}
`;
export const DeleteEmailTemplate = `
mutation deleteEmailTemplate($params: DeleteEmailTemplateRequest!) {
_delete_email_template(params: $params) {
message
}
}
`;

View File

@@ -0,0 +1,199 @@
export const MetaQuery = `
query MetaQuery {
meta {
version
client_id
}
}
`;
export const AdminSessionQuery = `
query {
_admin_session{
message
}
}
`;
export const EnvVariablesQuery = `
query {
_env{
CLIENT_ID
CLIENT_SECRET
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
DISCORD_CLIENT_ID
DISCORD_CLIENT_SECRET
TWITTER_CLIENT_ID
TWITTER_CLIENT_SECRET
MICROSOFT_CLIENT_ID
MICROSOFT_CLIENT_SECRET
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
TWITCH_CLIENT_ID
TWITCH_CLIENT_SECRET
ROBLOX_CLIENT_ID
ROBLOX_CLIENT_SECRET
DEFAULT_ROLES
PROTECTED_ROLES
ROLES
JWT_TYPE
JWT_SECRET
JWT_ROLE_CLAIM
JWT_PRIVATE_KEY
JWT_PUBLIC_KEY
REDIS_URL
SMTP_HOST
SMTP_PORT
SMTP_USERNAME
SMTP_PASSWORD
SMTP_LOCAL_NAME
SENDER_EMAIL
SENDER_NAME
ALLOWED_ORIGINS
ORGANIZATION_NAME
ORGANIZATION_LOGO
ADMIN_SECRET
APP_COOKIE_SECURE
ADMIN_COOKIE_SECURE
DISABLE_LOGIN_PAGE
DISABLE_MAGIC_LINK_LOGIN
DISABLE_EMAIL_VERIFICATION
DISABLE_BASIC_AUTHENTICATION
DISABLE_MOBILE_BASIC_AUTHENTICATION
DISABLE_SIGN_UP
DISABLE_STRONG_PASSWORD
DISABLE_REDIS_FOR_ENV
CUSTOM_ACCESS_TOKEN_SCRIPT
DATABASE_NAME
DATABASE_TYPE
DATABASE_URL
ACCESS_TOKEN_EXPIRY_TIME
DISABLE_MULTI_FACTOR_AUTHENTICATION
ENFORCE_MULTI_FACTOR_AUTHENTICATION
DEFAULT_AUTHORIZE_RESPONSE_TYPE
DEFAULT_AUTHORIZE_RESPONSE_MODE
DISABLE_PLAYGROUND
DISABLE_TOTP_LOGIN
DISABLE_MAIL_OTP_LOGIN
}
}
`;
export const UserDetailsQuery = `
query($params: PaginatedInput) {
_users(params: $params) {
pagination {
limit
page
offset
total
}
users {
id
email
email_verified
phone_number_verified
given_name
family_name
middle_name
nickname
gender
birthdate
phone_number
picture
signup_methods
roles
created_at
revoked_timestamp
is_multi_factor_auth_enabled
}
}
}
`;
export const EmailVerificationQuery = `
query {
_env{
DISABLE_EMAIL_VERIFICATION
}
}
`;
export const WebhooksDataQuery = `
query getWebhooksData($params: PaginatedInput!) {
_webhooks(params: $params){
webhooks{
id
event_description
event_name
endpoint
enabled
headers
}
pagination{
limit
page
offset
total
}
}
}
`;
export const EmailTemplatesQuery = `
query getEmailTemplates($params: PaginatedInput!) {
_email_templates(params: $params) {
email_templates {
id
event_name
subject
created_at
template
design
}
pagination {
limit
page
offset
total
}
}
}
`;
export const WebhookLogsQuery = `
query getWebhookLogs($params: ListWebhookLogRequest!) {
_webhook_logs(params: $params) {
webhook_logs {
id
http_status
request
response
created_at
}
pagination {
limit
page
offset
total
}
}
}
`;
export const GetAvailableRolesQuery = `
query {
_env {
ROLES
PROTECTED_ROLES
}
}
`;

10
dashboard/src/index.tsx Normal file
View File

@@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<div>
<App />
</div>,
document.getElementById('root'),
);

View File

@@ -0,0 +1,58 @@
import {
Box,
Flex,
Image,
Text,
Spinner,
useMediaQuery,
} from '@chakra-ui/react';
import React from 'react';
import { useQuery } from 'urql';
import { MetaQuery } from '../graphql/queries';
export function AuthLayout({ children }: { children: React.ReactNode }) {
const [{ fetching, data }] = useQuery({ query: MetaQuery });
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<Flex
h="100vh"
bg="gray.100"
alignItems="center"
justifyContent="center"
direction={['column', 'column']}
padding={['2%', '2%', '2%', '2%']}
>
<Flex alignItems="center" maxW="100%">
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
height="50"
/>
<Text fontSize="x-large" ml="3" letterSpacing="3">
AUTHORIZER
</Text>
</Flex>
{fetching ? (
<Spinner />
) : (
<>
<Box
p="6"
m="5"
rounded="5"
bg="white"
w={isNotSmallerScreen ? '500px' : '450px'}
shadow="xl"
maxW="100%"
>
{children}
</Box>
<Text color="gray.600" fontSize="sm">
Current Version: {data.meta.version}
</Text>
</>
)}
</Flex>
);
}

View File

@@ -0,0 +1,39 @@
import {
Box,
Drawer,
DrawerContent,
useDisclosure,
useColorModeValue,
} from '@chakra-ui/react';
import React, { ReactNode } from 'react';
import { Sidebar, MobileNav } from '../components/Menu';
export function DashboardLayout({ children }: { children: ReactNode }) {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<Box minH="100vh" bg={useColorModeValue('gray.100', 'gray.900')}>
<Sidebar
onClose={() => onClose}
display={{ base: 'none', md: 'block' }}
/>
<Drawer
autoFocus={false}
isOpen={isOpen}
placement="left"
onClose={onClose}
returnFocusOnClose={false}
onOverlayClick={onClose}
size="full"
>
<DrawerContent>
<Sidebar onClose={onClose} />
</DrawerContent>
</Drawer>
{/* mobilenav */}
<MobileNav onOpen={onOpen} />
<Box ml={{ base: 0, md: '64' }} p="4" pt="24">
{children}
</Box>
</Box>
);
}

View File

@@ -0,0 +1,128 @@
import {
Button,
FormControl,
FormLabel,
Input,
useToast,
VStack,
Text,
} from '@chakra-ui/react';
import React, { useEffect } from 'react';
import { useMutation } from 'urql';
import { AuthLayout } from '../layouts/AuthLayout';
import { AdminLogin, AdminSignup } from '../graphql/mutation';
import { useNavigate } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
import { capitalizeFirstLetter, hasAdminSecret } from '../utils';
export default function Auth() {
const [loginResult, login] = useMutation(AdminLogin);
const [signUpResult, signup] = useMutation(AdminSignup);
const { setIsLoggedIn } = useAuthContext();
const toast = useToast();
const navigate = useNavigate();
const isLogin = hasAdminSecret();
const handleSubmit = (e: any) => {
e.preventDefault();
const formValues = [...e.target.elements].reduce((agg: any, elem: any) => {
if (elem.id) {
return {
...agg,
[elem.id]: elem.value,
};
}
return agg;
}, {});
(isLogin ? login : signup)({
secret: formValues['admin-secret'],
}).then((res) => {
if (res.data) {
setIsLoggedIn(true);
navigate('/', { replace: true });
}
});
};
const errors = isLogin ? loginResult.error : signUpResult.error;
useEffect(() => {
if (errors?.graphQLErrors) {
(errors?.graphQLErrors || []).map((error: any) => {
toast({
title: capitalizeFirstLetter(error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
});
}
}, [errors]);
return (
<AuthLayout>
<Text
fontSize="large"
textAlign="center"
color="gray.600"
fontWeight="bold"
mb="2"
>
Hello Admin 👋 <br />
</Text>
<Text fontSize="large" textAlign="center" color="gray.500" mb="8">
Welcome to Admin Dashboard
</Text>
<form onSubmit={handleSubmit}>
<VStack spacing="5" justify="space-between">
<FormControl isRequired>
<FormLabel htmlFor="admin-username">Username</FormLabel>
<Input
size="lg"
id="admin-username"
placeholder="Username"
disabled
value="admin"
/>
</FormControl>
<FormControl isRequired>
<FormLabel htmlFor="admin-secret">Password</FormLabel>
<Input
size="lg"
id="admin-secret"
placeholder="Admin secret"
type="password"
minLength={!isLogin ? 6 : 1}
/>
</FormControl>
<Button
isLoading={signUpResult.fetching || loginResult.fetching}
loadingText="Submitting"
colorScheme="blue"
size="lg"
w="100%"
type="submit"
>
{isLogin ? 'Login' : 'Sign up'}
</Button>
{isLogin ? (
<Text color="gray.600" fontSize="sm">
<b>Note:</b> In case if you have forgot your admin secret, you can
reset it by updating <code>ADMIN_SECRET</code> environment
variable. For more information, please refer to the{' '}
<a href="https://docs.authorizer.dev/core/env/">documentation</a>.
</Text>
) : (
<Text color="gray.600" fontSize="sm">
<b>Note:</b> Configure the password to start using your dashboard.
</Text>
)}
</VStack>
</form>
</AuthLayout>
);
}

View File

@@ -0,0 +1,348 @@
import React, { useEffect, useState } from 'react';
import { useClient } from 'urql';
import {
Box,
Button,
Center,
Flex,
IconButton,
Menu,
MenuButton,
MenuList,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
Spinner,
Table,
TableCaption,
Tbody,
Td,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import {
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaAngleDown,
FaAngleLeft,
FaAngleRight,
FaExclamationCircle,
} from 'react-icons/fa';
import UpdateEmailTemplateModal from '../components/UpdateEmailTemplateModal';
import {
pageLimits,
UpdateModalViews,
EmailTemplateInputDataFields,
} from '../constants';
import { EmailTemplatesQuery } from '../graphql/queries';
import dayjs from 'dayjs';
import DeleteEmailTemplateModal from '../components/DeleteEmailTemplateModal';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface EmailTemplateDataType {
[EmailTemplateInputDataFields.ID]: string;
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.CREATED_AT]: number;
[EmailTemplateInputDataFields.TEMPLATE]: string;
[EmailTemplateInputDataFields.DESIGN]: string;
}
const EmailTemplates = () => {
const client = useClient();
const [loading, setLoading] = useState<boolean>(false);
const [emailTemplatesData, setEmailTemplatesData] = useState<
EmailTemplateDataType[]
>([]);
const [paginationProps, setPaginationProps] = useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const fetchEmailTemplatesData = async () => {
setLoading(true);
const res = await client
.query(EmailTemplatesQuery, {
params: {
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (res.data?._email_templates) {
const { pagination, email_templates: emailTemplates } =
res.data?._email_templates;
const maxPages = getMaxPages(pagination);
if (emailTemplates?.length) {
setEmailTemplatesData(emailTemplates);
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
useEffect(() => {
fetchEmailTemplatesData();
}, [paginationProps.page, paginationProps.limit]);
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
<Text fontSize="md" fontWeight="bold">
Email Templates
</Text>
<UpdateEmailTemplateModal
view={UpdateModalViews.ADD}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
</Flex>
{!loading ? (
emailTemplatesData.length ? (
<Table variant="simple">
<Thead>
<Tr>
<Th>Event Name</Th>
<Th>Subject</Th>
<Th>Created At</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{emailTemplatesData.map((templateData: EmailTemplateDataType) => (
<Tr
key={templateData[EmailTemplateInputDataFields.ID]}
style={{ fontSize: 14 }}
>
<Td maxW="300">
{templateData[EmailTemplateInputDataFields.EVENT_NAME]}
</Td>
<Td>{templateData[EmailTemplateInputDataFields.SUBJECT]}</Td>
<Td>
{dayjs(templateData.created_at * 1000).format(
'MMM DD, YYYY',
)}
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
<UpdateEmailTemplateModal
view={UpdateModalViews.Edit}
selectedTemplate={templateData}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
<DeleteEmailTemplateModal
emailTemplateId={
templateData[EmailTemplateInputDataFields.ID]
}
eventName={
templateData[
EmailTemplateInputDataFields.EVENT_NAME
]
}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
</MenuList>
</Menu>
</Td>
</Tr>
))}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{pageLimits.map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Box>
);
};
export default EmailTemplates;

View File

@@ -0,0 +1,348 @@
import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { Box, Flex, Stack, Button, useToast } from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa';
import _ from 'lodash';
import { EnvVariablesQuery } from '../graphql/queries';
import {
SelectInputType,
HiddenInputType,
TextInputType,
HMACEncryptionType,
RSAEncryptionType,
ECDSAEncryptionType,
envVarTypes,
envSubViews,
} from '../constants';
import { UpdateEnvVariables } from '../graphql/mutation';
import { getObjectDiff, capitalizeFirstLetter } from '../utils';
import OAuthConfig from '../components/EnvComponents/OAuthConfig';
import Roles from '../components/EnvComponents/Roles';
import JWTConfigurations from '../components/EnvComponents/JWTConfiguration';
import SessionStorage from '../components/EnvComponents/SessionStorage';
import EmailConfigurations from '../components/EnvComponents/EmailConfiguration';
import DomainWhiteListing from '../components/EnvComponents/DomainWhitelisting';
import OrganizationInfo from '../components/EnvComponents/OrganizationInfo';
import AccessToken from '../components/EnvComponents/AccessToken';
import Features from '../components/EnvComponents/Features';
import SecurityAdminSecret from '../components/EnvComponents/SecurityAdminSecret';
import DatabaseCredentials from '../components/EnvComponents/DatabaseCredentials';
const Environment = () => {
const client = useClient();
const toast = useToast();
const [adminSecret, setAdminSecret] = React.useState<
Record<string, string | boolean>
>({
value: '',
disableInputField: true,
});
const [loading, setLoading] = React.useState<boolean>(true);
const [envVariables, setEnvVariables] = React.useState<envVarTypes>({
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: '',
DISCORD_CLIENT_ID: '',
DISCORD_CLIENT_SECRET: '',
TWITTER_CLIENT_ID: '',
TWITTER_CLIENT_SECRET: '',
MICROSOFT_CLIENT_ID: '',
MICROSOFT_CLIENT_SECRET: '',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: '',
TWITCH_CLIENT_ID: '',
TWITCH_CLIENT_SECRET: '',
ROBLOX_CLIENT_ID: '',
ROBLOX_CLIENT_SECRET: '',
ROLES: [],
DEFAULT_ROLES: [],
PROTECTED_ROLES: [],
JWT_TYPE: '',
JWT_SECRET: '',
JWT_ROLE_CLAIM: '',
JWT_PRIVATE_KEY: '',
JWT_PUBLIC_KEY: '',
REDIS_URL: '',
SMTP_HOST: '',
SMTP_PORT: '',
SMTP_USERNAME: '',
SMTP_PASSWORD: '',
SMTP_LOCAL_NAME: '',
SENDER_EMAIL: '',
SENDER_NAME: '',
ALLOWED_ORIGINS: [],
ORGANIZATION_NAME: '',
ORGANIZATION_LOGO: '',
CUSTOM_ACCESS_TOKEN_SCRIPT: '',
ADMIN_SECRET: '',
APP_COOKIE_SECURE: false,
ADMIN_COOKIE_SECURE: false,
DISABLE_LOGIN_PAGE: false,
DISABLE_MAGIC_LINK_LOGIN: false,
DISABLE_EMAIL_VERIFICATION: false,
DISABLE_BASIC_AUTHENTICATION: false,
DISABLE_MOBILE_BASIC_AUTHENTICATION: false,
DISABLE_SIGN_UP: false,
DISABLE_STRONG_PASSWORD: false,
OLD_ADMIN_SECRET: '',
DATABASE_NAME: '',
DATABASE_TYPE: '',
DATABASE_URL: '',
ACCESS_TOKEN_EXPIRY_TIME: '',
DISABLE_MULTI_FACTOR_AUTHENTICATION: false,
ENFORCE_MULTI_FACTOR_AUTHENTICATION: false,
DEFAULT_AUTHORIZE_RESPONSE_TYPE: '',
DEFAULT_AUTHORIZE_RESPONSE_MODE: '',
DISABLE_PLAYGROUND: false,
DISABLE_TOTP_LOGIN: false,
DISABLE_MAIL_OTP_LOGIN: true,
});
const [fieldVisibility, setFieldVisibility] = React.useState<
Record<string, boolean>
>({
GOOGLE_CLIENT_SECRET: false,
GITHUB_CLIENT_SECRET: false,
FACEBOOK_CLIENT_SECRET: false,
LINKEDIN_CLIENT_SECRET: false,
APPLE_CLIENT_SECRET: false,
DISCORD_CLIENT_SECRET: false,
TWITTER_CLIENT_SECRET: false,
TWITCH_CLIENT_SECRET: false,
JWT_SECRET: false,
SMTP_PASSWORD: false,
ADMIN_SECRET: false,
OLD_ADMIN_SECRET: false,
});
const { sec } = useParams();
async function getData() {
const {
data: { _env: envData },
} = await client.query(EnvVariablesQuery).toPromise();
setLoading(false);
setEnvVariables({
...envData,
OLD_ADMIN_SECRET: envData.ADMIN_SECRET,
ADMIN_SECRET: '',
});
setAdminSecret({
value: '',
disableInputField: true,
});
}
useEffect(() => {
getData();
}, [sec]);
const validateAdminSecretHandler = (event: any) => {
if (envVariables.OLD_ADMIN_SECRET === event.target.value) {
setAdminSecret({
...adminSecret,
value: event.target.value,
disableInputField: false,
});
} else {
setAdminSecret({
...adminSecret,
value: event.target.value,
disableInputField: true,
});
}
if (envVariables.ADMIN_SECRET !== '') {
setEnvVariables({ ...envVariables, ADMIN_SECRET: '' });
}
};
const saveHandler = async () => {
setLoading(true);
const {
data: { _env: envData },
} = await client.query(EnvVariablesQuery).toPromise();
const diff = getObjectDiff(envVariables, envData);
const updatedEnvVariables = diff.reduce(
(acc: any, property: string) => ({
...acc,
// @ts-ignore
[property]: envVariables[property],
}),
{},
);
if (
updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' ||
updatedEnvVariables[HiddenInputType.OLD_ADMIN_SECRET] !==
envData.ADMIN_SECRET
) {
delete updatedEnvVariables.OLD_ADMIN_SECRET;
delete updatedEnvVariables.ADMIN_SECRET;
}
delete updatedEnvVariables.DATABASE_URL;
delete updatedEnvVariables.DATABASE_TYPE;
delete updatedEnvVariables.DATABASE_NAME;
const res = await client
.mutation(UpdateEnvVariables, { params: updatedEnvVariables })
.toPromise();
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
});
return;
}
setAdminSecret({
value: '',
disableInputField: true,
});
getData();
toast({
title: `Successfully updated ${
Object.keys(updatedEnvVariables).length
} variables`,
isClosable: true,
status: 'success',
position: 'top-right',
});
};
const renderComponent = (tab: any) => {
switch (tab) {
case envSubViews.INSTANCE_INFO:
return (
<OAuthConfig
envVariables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
/>
);
case envSubViews.ROLES:
return (
<Roles variables={envVariables} setVariables={setEnvVariables} />
);
case envSubViews.JWT_CONFIG:
return (
<JWTConfigurations
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
SelectInputType={SelectInputType.JWT_TYPE}
HMACEncryptionType={HMACEncryptionType}
RSAEncryptionType={RSAEncryptionType}
ECDSAEncryptionType={ECDSAEncryptionType}
getData={getData}
/>
);
case envSubViews.SESSION_STORAGE:
return (
<SessionStorage
variables={envVariables}
setVariables={setEnvVariables}
RedisURL={TextInputType.REDIS_URL}
/>
);
case envSubViews.EMAIL_CONFIG:
return (
<EmailConfigurations
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
/>
);
case envSubViews.WHITELIST_VARIABLES:
return (
<DomainWhiteListing
variables={envVariables}
setVariables={setEnvVariables}
/>
);
case envSubViews.ORGANIZATION_INFO:
return (
<OrganizationInfo
variables={envVariables}
setVariables={setEnvVariables}
/>
);
case envSubViews.ACCESS_TOKEN:
return (
<AccessToken
variables={envVariables}
setVariables={setEnvVariables}
/>
);
case envSubViews.FEATURES:
return (
<Features variables={envVariables} setVariables={setEnvVariables} />
);
case envSubViews.ADMIN_SECRET:
return (
<SecurityAdminSecret
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
validateAdminSecretHandler={validateAdminSecretHandler}
adminSecret={adminSecret}
/>
);
case envSubViews.DB_CRED:
return (
<DatabaseCredentials
variables={envVariables}
setVariables={setEnvVariables}
/>
);
default:
return (
<OAuthConfig
envVariables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
/>
);
}
};
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
{renderComponent(sec)}
<Stack spacing={6} padding="1% 0" mt={4}>
<Flex justifyContent="end" alignItems="center">
<Button
leftIcon={<FaSave />}
colorScheme="blue"
variant="solid"
onClick={saveHandler}
isDisabled={loading}
>
Save
</Button>
</Flex>
</Stack>
</Box>
);
};
export default Environment;

View File

@@ -0,0 +1,18 @@
import { Text } from '@chakra-ui/react';
import React from 'react';
export default function Home() {
return (
<>
<Text fontSize="2xl" fontWeight="bold">
Hi there 👋 <br />
</Text>
<Text fontSize="xl" color="gray.700">
Welcome to Authorizer Administrative Dashboard! <br />
Please use this dashboard to configure your environment variables or
have look at your users
</Text>
</>
);
}

View File

@@ -0,0 +1,607 @@
import React from 'react';
import { useClient } from 'urql';
import dayjs from 'dayjs';
import {
Box,
Flex,
IconButton,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
Table,
Tag,
Tbody,
Td,
Text,
TableCaption,
Th,
Thead,
Tooltip,
Tr,
Button,
Center,
Menu,
MenuButton,
MenuList,
MenuItem,
useToast,
Spinner,
TableContainer,
} from '@chakra-ui/react';
import {
FaAngleLeft,
FaAngleRight,
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaExclamationCircle,
FaAngleDown,
} from 'react-icons/fa';
import { EmailVerificationQuery, UserDetailsQuery } from '../graphql/queries';
import { EnableAccess, RevokeAccess, UpdateUser } from '../graphql/mutation';
import EditUserModal from '../components/EditUserModal';
import DeleteUserModal from '../components/DeleteUserModal';
import InviteMembersModal from '../components/InviteMembersModal';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface userDataTypes {
id: string;
email: string;
email_verified: boolean;
given_name: string;
family_name: string;
middle_name: string;
nickname: string;
gender: string;
birthdate: string;
phone_number: string;
picture: string;
signup_methods: string;
roles: [string];
created_at: number;
revoked_timestamp: number;
is_multi_factor_auth_enabled?: boolean;
}
const enum updateAccessActions {
REVOKE = 'REVOKE',
ENABLE = 'ENABLE',
}
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const getLimits = (pagination: paginationPropTypes) => {
const { total } = pagination;
const limits = [5];
if (total > 10) {
for (let i = 10; i <= total && limits.length <= 10; i += 5) {
limits.push(i);
}
}
return limits;
};
export default function Users() {
const client = useClient();
const toast = useToast();
const [paginationProps, setPaginationProps] =
React.useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const [userList, setUserList] = React.useState<userDataTypes[]>([]);
const [loading, setLoading] = React.useState<boolean>(false);
const [disableInviteMembers, setDisableInviteMembers] =
React.useState<boolean>(true);
const updateUserList = async () => {
setLoading(true);
const { data } = await client
.query(UserDetailsQuery, {
params: {
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (data?._users) {
const { pagination, users } = data._users;
const maxPages = getMaxPages(pagination);
if (users && users.length > 0) {
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
setUserList(users);
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const checkEmailVerification = async () => {
setLoading(true);
const { data } = await client.query(EmailVerificationQuery).toPromise();
if (data?._env) {
const { DISABLE_EMAIL_VERIFICATION } = data._env;
setDisableInviteMembers(DISABLE_EMAIL_VERIFICATION);
}
setLoading(false);
};
React.useEffect(() => {
updateUserList();
checkEmailVerification();
}, []);
React.useEffect(() => {
updateUserList();
}, [paginationProps.page, paginationProps.limit]);
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
const userVerificationHandler = async (user: userDataTypes) => {
const { id, email, phone_number } = user;
let params = {};
if (email) {
params = {
id,
email,
email_verified: true,
};
}
if (phone_number) {
params = {
id,
phone_number,
phone_number_verified: true,
};
}
const res = await client
.mutation(UpdateUser, {
params,
})
.toPromise();
if (res.error) {
toast({
title: 'User verification failed',
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (res.data?._update_user?.id) {
toast({
title: 'User verification successful',
isClosable: true,
status: 'success',
position: 'top-right',
});
}
updateUserList();
};
const updateAccessHandler = async (
id: string,
action: updateAccessActions,
) => {
switch (action) {
case updateAccessActions.ENABLE:
const enableAccessRes = await client
.mutation(EnableAccess, {
param: {
user_id: id,
},
})
.toPromise();
if (enableAccessRes.error) {
toast({
title: 'User access enable failed',
isClosable: true,
status: 'error',
position: 'top-right',
});
} else {
toast({
title: 'User access enabled successfully',
isClosable: true,
status: 'success',
position: 'top-right',
});
}
updateUserList();
break;
case updateAccessActions.REVOKE:
const revokeAccessRes = await client
.mutation(RevokeAccess, {
param: {
user_id: id,
},
})
.toPromise();
if (revokeAccessRes.error) {
toast({
title: 'User access revoke failed',
isClosable: true,
status: 'error',
position: 'top-right',
});
} else {
toast({
title: 'User access revoked successfully',
isClosable: true,
status: 'success',
position: 'top-right',
});
}
updateUserList();
break;
default:
break;
}
};
const multiFactorAuthUpdateHandler = async (user: userDataTypes) => {
const res = await client
.mutation(UpdateUser, {
params: {
id: user.id,
is_multi_factor_auth_enabled: !user.is_multi_factor_auth_enabled,
},
})
.toPromise();
if (res.data?._update_user?.id) {
toast({
title: `Multi factor authentication ${
user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
} for user`,
isClosable: true,
status: 'success',
position: 'top-right',
});
updateUserList();
return;
}
toast({
title: 'Multi factor authentication update failed for user',
isClosable: true,
status: 'error',
position: 'top-right',
});
};
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
<Text fontSize="md" fontWeight="bold">
Users
</Text>
<InviteMembersModal
disabled={disableInviteMembers}
updateUserList={updateUserList}
/>
</Flex>
{!loading ? (
userList.length > 0 ? (
<TableContainer>
<Table variant="simple">
<Thead>
<Tr>
<Th>Email / Phone</Th>
<Th>Created At</Th>
<Th>Signup Methods</Th>
<Th>Roles</Th>
<Th>Verified</Th>
<Th>Access</Th>
<Th>
<Tooltip label="MultiFactor Authentication Enabled / Disabled">
MFA
</Tooltip>
</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{userList.map((user: userDataTypes) => {
const {
email_verified,
phone_number_verified,
created_at,
...rest
}: any = user;
return (
<Tr key={user.id} style={{ fontSize: 14 }}>
<Td maxW="300">{user.email || user.phone_number}</Td>
<Td>
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
</Td>
<Td>{user.signup_methods}</Td>
<Td>{user.roles.join(', ')}</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
user.email_verified || user.phone_number_verified
? 'green'
: 'yellow'
}
>
{(
user.email_verified || user.phone_number_verified
).toString()}
</Tag>
</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
>
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
</Tag>
</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
user.is_multi_factor_auth_enabled ? 'green' : 'red'
}
>
{user.is_multi_factor_auth_enabled
? 'Enabled'
: 'Disabled'}
</Tag>
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
{!user.email_verified &&
!user.phone_number_verified && (
<MenuItem
onClick={() => userVerificationHandler(user)}
>
Verify User
</MenuItem>
)}
<EditUserModal
user={rest}
updateUserList={updateUserList}
/>
<DeleteUserModal
user={rest}
updateUserList={updateUserList}
/>
{user.revoked_timestamp ? (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.ENABLE,
)
}
>
Enable Access
</MenuItem>
) : (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.REVOKE,
)
}
>
Revoke Access
</MenuItem>
)}
{user.is_multi_factor_auth_enabled ? (
<MenuItem
onClick={() =>
multiFactorAuthUpdateHandler(user)
}
>
Disable MultiFactor Authentication
</MenuItem>
) : (
<MenuItem
onClick={() =>
multiFactorAuthUpdateHandler(user)
}
>
Enable MultiFactor Authentication
</MenuItem>
)}
</MenuList>
</Menu>
</Td>
</Tr>
);
})}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{getLimits(paginationProps).map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
</TableContainer>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Box>
);
}

View File

@@ -0,0 +1,374 @@
import React, { useEffect, useState } from 'react';
import { useClient } from 'urql';
import {
Box,
Button,
Center,
Flex,
IconButton,
Menu,
MenuButton,
MenuList,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
Spinner,
Table,
TableCaption,
Tag,
Tbody,
Td,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import {
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaAngleDown,
FaAngleLeft,
FaAngleRight,
FaExclamationCircle,
} from 'react-icons/fa';
import UpdateWebhookModal from '../components/UpdateWebhookModal';
import {
pageLimits,
WebhookInputDataFields,
UpdateModalViews,
} from '../constants';
import { WebhooksDataQuery } from '../graphql/queries';
import DeleteWebhookModal from '../components/DeleteWebhookModal';
import ViewWebhookLogsModal from '../components/ViewWebhookLogsModal';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface webhookDataTypes {
[WebhookInputDataFields.ID]: string;
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
}
const Webhooks = () => {
const client = useClient();
const [loading, setLoading] = useState<boolean>(false);
const [webhookData, setWebhookData] = useState<webhookDataTypes[]>([]);
const [paginationProps, setPaginationProps] = useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const fetchWebookData = async () => {
setLoading(true);
const res = await client
.query(WebhooksDataQuery, {
params: {
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (res.data?._webhooks) {
const { pagination, webhooks } = res.data?._webhooks;
const maxPages = getMaxPages(pagination);
if (webhooks?.length) {
setWebhookData(webhooks);
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
useEffect(() => {
fetchWebookData();
}, [paginationProps.page, paginationProps.limit]);
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
<Text fontSize="md" fontWeight="bold">
Webhooks
</Text>
<UpdateWebhookModal
view={UpdateModalViews.ADD}
fetchWebookData={fetchWebookData}
/>
</Flex>
{!loading ? (
webhookData.length ? (
<Table variant="simple">
<Thead>
<Tr>
<Th>Event Name</Th>
<Th>Event Description</Th>
<Th>Endpoint</Th>
<Th>Enabled</Th>
<Th>Headers</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{webhookData.map((webhook: webhookDataTypes) => (
<Tr
key={webhook[WebhookInputDataFields.ID]}
style={{ fontSize: 14 }}
>
<Td maxW="300">
{webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]}
</Td>
<Td maxW="300">
{webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
</Td>
<Td>{webhook[WebhookInputDataFields.ENDPOINT]}</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
webhook[WebhookInputDataFields.ENABLED]
? 'green'
: 'yellow'
}
>
{webhook[WebhookInputDataFields.ENABLED].toString()}
</Tag>
</Td>
<Td>
<Tooltip
bg="gray.300"
color="black"
label={JSON.stringify(
webhook[WebhookInputDataFields.HEADERS],
null,
' ',
)}
>
<Tag size="sm" variant="outline" colorScheme="gray">
{Object.keys(
webhook[WebhookInputDataFields.HEADERS] || {},
)?.length.toString()}
</Tag>
</Tooltip>
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
<UpdateWebhookModal
view={UpdateModalViews.Edit}
selectedWebhook={webhook}
fetchWebookData={fetchWebookData}
/>
<DeleteWebhookModal
webhookId={webhook[WebhookInputDataFields.ID]}
eventName={webhook[WebhookInputDataFields.EVENT_NAME]}
fetchWebookData={fetchWebookData}
/>
<ViewWebhookLogsModal
webhookId={webhook[WebhookInputDataFields.ID]}
eventName={webhook[WebhookInputDataFields.EVENT_NAME]}
/>
</MenuList>
</Menu>
</Td>
</Tr>
))}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text>Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{pageLimits.map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Box>
);
};
export default Webhooks;

View File

@@ -0,0 +1,51 @@
import React, { lazy, Suspense } from 'react';
import { Outlet, Route, Routes } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
import { DashboardLayout } from '../layouts/DashboardLayout';
import EmailTemplates from '../pages/EmailTemplates';
const Auth = lazy(() => import('../pages/Auth'));
const Environment = lazy(() => import('../pages/Environment'));
const Home = lazy(() => import('../pages/Home'));
const Users = lazy(() => import('../pages/Users'));
const Webhooks = lazy(() => import('../pages/Webhooks'));
export const AppRoutes = () => {
const { isLoggedIn } = useAuthContext();
if (isLoggedIn) {
return (
<div>
<Suspense fallback={<></>}>
<Routes>
<Route
element={
<DashboardLayout>
<Outlet />
</DashboardLayout>
}
>
<Route path="/" element={<Outlet />}>
<Route index element={<Environment />} />
<Route path="/:sec" element={<Environment />} />
</Route>
<Route path="users" element={<Users />} />
<Route path="webhooks" element={<Webhooks />} />
<Route path="email-templates" element={<EmailTemplates />} />
<Route path="*" element={<Home />} />
</Route>
</Routes>
</Suspense>
</div>
);
}
return (
<Suspense fallback={<></>}>
<Routes>
<Route path="/" element={<Auth />} />
<Route path="*" element={<Auth />} />
</Routes>
</Suspense>
);
};

View File

@@ -0,0 +1,85 @@
import _ from 'lodash';
export const hasAdminSecret = () => {
return (<any>window)['__authorizer__'].isOnboardingCompleted === true;
};
export const capitalizeFirstLetter = (data: string): string =>
data.charAt(0).toUpperCase() + data.slice(1);
const fallbackCopyTextToClipboard = (text: string) => {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.top = '0';
textArea.style.left = '0';
textArea.style.position = 'fixed';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
const msg = successful ? 'successful' : 'unsuccessful';
console.log('Fallback: Copying text command was ' + msg);
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
}
document.body.removeChild(textArea);
};
export const copyTextToClipboard = async (text: string) => {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text);
return;
}
try {
navigator.clipboard.writeText(text);
} catch (err) {
throw err;
}
};
export const getObjectDiff = (obj1: any, obj2: any) => {
const diff = Object.keys(obj1).reduce((result, key) => {
if (!obj2.hasOwnProperty(key)) {
result.push(key);
} else if (
_.isEqual(obj1[key], obj2[key]) ||
(obj1[key] === null && obj2[key] === '') ||
(obj1[key] &&
Array.isArray(obj1[key]) &&
obj1[key].length === 0 &&
obj2[key] === null)
) {
const resultKeyIndex = result.indexOf(key);
result.splice(resultKeyIndex, 1);
}
return result;
}, Object.keys(obj2));
return diff;
};
export const validateEmail = (email: string) => {
if (!email || email === '') return true;
return email
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
)
? true
: false;
};
export const validateURI = (uri: string) => {
if (!uri || uri === '') return true;
return uri
.toLowerCase()
.match(
/(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/,
)
? true
: false;
};

View File

@@ -0,0 +1,39 @@
import _flatten from 'lodash/flatten';
import { validateEmail } from '.';
interface dataTypes {
value: string;
isInvalid: boolean;
}
const parseCSV = (file: File, delimiter: string): Promise<dataTypes[]> => {
return new Promise((resolve) => {
const reader = new FileReader();
// When the FileReader has loaded the file...
reader.onload = (e: any) => {
// Split the result to an array of lines
const lines = e.target.result.split('\n');
// Split the lines themselves by the specified
// delimiter, such as a comma
let result = lines.map((line: string) => line.split(delimiter));
// As the FileReader reads asynchronously,
// we can't just return the result; instead,
// we're passing it to a callback function
result = _flatten(result);
resolve(
result.map((email: string) => {
return {
value: email.trim(),
isInvalid: !validateEmail(email.trim()),
};
}),
);
};
// Read the file content as a single string
reader.readAsText(file);
});
};
export default parseCSV;

73
dashboard/tsconfig.json Normal file
View File

@@ -0,0 +1,73 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
// "lib": ["es2018", "dom"], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */,
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
"rootDir": "src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"lib": ["esnext", "dom"]
}
}

1887
dashboard/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

20
go.mod Normal file
View File

@@ -0,0 +1,20 @@
module server
go 1.21.5
require (
github.com/99designs/gqlgen v0.17.43 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sosodev/duration v1.1.0 // indirect
github.com/urfave/cli/v2 v2.25.5 // indirect
github.com/vektah/gqlparser/v2 v2.5.11 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.9.3 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

31
go.sum Normal file
View File

@@ -0,0 +1,31 @@
github.com/99designs/gqlgen v0.17.43 h1:I4SYg6ahjowErAQcHFVKy5EcWuwJ3+Xw9z2fLpuFCPo=
github.com/99designs/gqlgen v0.17.43/go.mod h1:lO0Zjy8MkZgBdv4T1U91x09r0e0WFOdhVUutlQs1Rsc=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sosodev/duration v1.1.0 h1:kQcaiGbJaIsRqgQy7VGlZrVw1giWO+lDoX3MCPnpVO4=
github.com/sosodev/duration v1.1.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

6
package-lock.json generated Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "authorizer",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}

View File

@@ -1,7 +1,7 @@
VERSION="$1" VERSION="$1"
make clean && make build-app && CGO_ENABLED=1 VERSION=${VERSION} make make clean && make build-app && CGO_ENABLED=1 VERSION=${VERSION} make
FILE_NAME=authorizer-${VERSION}-darwin-amd64.tar.gz FILE_NAME=authorizer-${VERSION}-darwin-amd64.tar.gz
tar cvfz ${FILE_NAME} .env app/build build templates tar cvfz ${FILE_NAME} .env app/build build templates dashboard/build
AUTH="Authorization: token $GITHUB_TOKEN" AUTH="Authorization: token $GITHUB_TOKEN"
RELASE_INFO=$(curl -sH "$AUTH" https://api.github.com/repos/authorizerdev/authorizer/releases/tags/${VERSION}) RELASE_INFO=$(curl -sH "$AUTH" https://api.github.com/repos/authorizerdev/authorizer/releases/tags/${VERSION})
echo $RELASE_INFO echo $RELASE_INFO

39
scripts/couchbase-test.sh Normal file
View File

@@ -0,0 +1,39 @@
#!/bin/sh
set -x
set -m
sleep 15
# Setup index and memory quota
# curl -v -X POST http://127.0.0.1:8091/pools/default -d memoryQuota=300 -d indexMemoryQuota=300
# Setup services
curl -v http://127.0.0.1:8091/node/controller/setupServices -d services=kv%2Cn1ql%2Cindex
# Setup credentials
curl -v http://127.0.0.1:8091/settings/web -d port=8091 -d username=Administrator -d password=password
# Setup Memory Optimized Indexes
curl -i -u Administrator:password -X POST http://127.0.0.1:8091/settings/indexes -d 'storageMode=memory_optimized'
# Load travel-sample bucket
#curl -v -u Administrator:password -X POST http://127.0.0.1:8091/sampleBuckets/install -d '["travel-sample"]'
echo "Type: $TYPE"
if [ "$TYPE" = "WORKER" ]; then
echo "Sleeping ..."
sleep 15
#IP=`hostname -s`
IP=`hostname -I | cut -d ' ' -f1`
echo "IP: " $IP
echo "Auto Rebalance: $AUTO_REBALANCE"
if [ "$AUTO_REBALANCE" = "true" ]; then
couchbase-cli rebalance --cluster=$COUCHBASE_MASTER:8091 --user=Administrator --password=password --server-add=$IP --server-add-username=Administrator --server-add-password=password
else
couchbase-cli server-add --cluster=$COUCHBASE_MASTER:8091 --user=Administrator --password=password --server-add=$IP --server-add-username=Administrator --server-add-password=password
fi;
fi;

View File

@@ -1,34 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func deleteUserTest(s TestSetup, t *testing.T) {
t.Run(`should delete users with admin secret only`, func(t *testing.T) {
req, ctx := createContext(s)
email := "delete_user." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err := resolvers.DeleteUser(ctx, model.DeleteUserInput{
Email: email,
})
assert.NotNil(t, err, "unauthorized")
req.Header.Add("x-authorizer-admin-secret", constants.ADMIN_SECRET)
_, err = resolvers.DeleteUser(ctx, model.DeleteUserInput{
Email: email,
})
assert.Nil(t, err)
cleanData(email)
})
}

View File

@@ -1,25 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/stretchr/testify/assert"
)
func TestEnvs(t *testing.T) {
constants.ENV_PATH = "../../.env.sample"
assert.Equal(t, constants.ADMIN_SECRET, "admin")
assert.Equal(t, constants.ENV, "production")
assert.False(t, constants.DISABLE_EMAIL_VERIFICATION)
assert.False(t, constants.DISABLE_MAGIC_LINK_LOGIN)
assert.False(t, constants.DISABLE_BASIC_AUTHENTICATION)
assert.Equal(t, constants.JWT_TYPE, "HS256")
assert.Equal(t, constants.JWT_SECRET, "random_string")
assert.Equal(t, constants.JWT_ROLE_CLAIM, "role")
assert.EqualValues(t, constants.ROLES, []string{"user"})
assert.EqualValues(t, constants.DEFAULT_ROLES, []string{"user"})
assert.EqualValues(t, constants.PROTECTED_ROLES, []string{"admin"})
assert.EqualValues(t, constants.ALLOWED_ORIGINS, []string{"*"})
}

View File

@@ -1,35 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func forgotPasswordTest(s TestSetup, t *testing.T) {
t.Run(`should run forgot password`, func(t *testing.T) {
_, ctx := createContext(s)
email := "forgot_password." + s.TestInfo.Email
_, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.ForgotPassword(ctx, model.ForgotPasswordInput{
Email: email,
})
assert.Nil(t, err, "no errors for forgot password")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String())
assert.Nil(t, err)
assert.Equal(t, verificationRequest.Identifier, enum.ForgotPassword.String())
cleanData(email)
})
}

View File

@@ -1,58 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func loginTests(s TestSetup, t *testing.T) {
t.Run(`should login`, func(t *testing.T) {
_, ctx := createContext(s)
email := "login." + s.TestInfo.Email
_, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.Login(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
})
assert.NotNil(t, err, "should fail because email is not verified")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
_, err = resolvers.Login(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
Roles: []string{"test"},
})
assert.NotNil(t, err, "invalid roles")
_, err = resolvers.Login(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password + "s",
})
assert.NotNil(t, err, "invalid password")
loginRes, err := resolvers.Login(ctx, model.LoginInput{
Email: email,
Password: s.TestInfo.Password,
})
assert.Nil(t, err, "login successful")
assert.NotNil(t, loginRes.AccessToken, "access token should not be empty")
cleanData(email)
})
}

View File

@@ -1,35 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func logoutTests(s TestSetup, t *testing.T) {
t.Run(`should logout user`, func(t *testing.T) {
req, ctx := createContext(s)
email := "logout." + s.TestInfo.Email
_, err := resolvers.MagicLinkLogin(ctx, model.MagicLinkLoginInput{
Email: email,
})
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.MagicLinkLogin.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Add("Authorization", "Bearer "+token)
_, err = resolvers.Logout(ctx)
assert.Nil(t, err)
_, err = resolvers.Profile(ctx)
assert.NotNil(t, err, "unauthorized")
cleanData(email)
})
}

View File

@@ -1,35 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func magicLinkLoginTests(s TestSetup, t *testing.T) {
t.Run(`should login with magic link`, func(t *testing.T) {
req, ctx := createContext(s)
email := "magic_link_login." + s.TestInfo.Email
_, err := resolvers.MagicLinkLogin(ctx, model.MagicLinkLoginInput{
Email: email,
})
assert.Nil(t, err)
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.MagicLinkLogin.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Add("Authorization", "Bearer "+token)
_, err = resolvers.Profile(ctx)
assert.Nil(t, err)
cleanData(email)
})
}

View File

@@ -1,42 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func profileTests(s TestSetup, t *testing.T) {
t.Run(`should get profile only with token`, func(t *testing.T) {
req, ctx := createContext(s)
email := "profile." + s.TestInfo.Email
resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err := resolvers.Profile(ctx)
assert.NotNil(t, err, "unauthorized")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.BasicAuthSignup.String())
verifyRes, err := resolvers.VerifyEmail(ctx, model.VerifyEmailInput{
Token: verificationRequest.Token,
})
token := *verifyRes.AccessToken
req.Header.Add("Authorization", "Bearer "+token)
profileRes, err := resolvers.Profile(ctx)
assert.Nil(t, err)
newEmail := *&profileRes.Email
assert.Equal(t, email, newEmail, "emails should be equal")
cleanData(email)
})
}

View File

@@ -1,49 +0,0 @@
package test
import (
"testing"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/enum"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/stretchr/testify/assert"
)
func resetPasswordTest(s TestSetup, t *testing.T) {
t.Run(`should reset password`, func(t *testing.T) {
email := "reset_password." + s.TestInfo.Email
_, ctx := createContext(s)
_, err := resolvers.Signup(ctx, model.SignUpInput{
Email: email,
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
_, err = resolvers.ForgotPassword(ctx, model.ForgotPasswordInput{
Email: email,
})
assert.Nil(t, err, "no errors for forgot password")
verificationRequest, err := db.Mgr.GetVerificationByEmail(email, enum.ForgotPassword.String())
assert.Nil(t, err, "should get forgot password request")
_, err = resolvers.ResetPassword(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token,
Password: "test1",
ConfirmPassword: "test",
})
assert.NotNil(t, err, "passowrds don't match")
_, err = resolvers.ResetPassword(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token,
Password: "test1",
ConfirmPassword: "test1",
})
assert.Nil(t, err, "password changed successfully")
cleanData(email)
})
}

Some files were not shown because too many files have changed in this diff Show More