Compare commits
53 Commits
feat/send-
...
feat/dyanm
Author | SHA1 | Date | |
---|---|---|---|
![]() |
25cb9a831b | ||
![]() |
19e2153379 | ||
![]() |
221009bf0a | ||
![]() |
6085c2d535 | ||
![]() |
8e0c5e4380 | ||
![]() |
21b70e4b26 | ||
![]() |
993693884d | ||
![]() |
ed849fa6f6 | ||
![]() |
aec1f5df53 | ||
![]() |
195bd1bc6a | ||
![]() |
45b4c41bca | ||
![]() |
63d486821e | ||
![]() |
4b56afdc98 | ||
![]() |
6455ff956a | ||
![]() |
3898e43fff | ||
![]() |
2c305e5bde | ||
![]() |
b8fd08e576 | ||
![]() |
6dafa45051 | ||
![]() |
ead3514113 | ||
![]() |
75a413e5f2 | ||
![]() |
91bf0e2478 | ||
![]() |
7a1305cf96 | ||
![]() |
ff5a6ec301 | ||
![]() |
b7b97b4f8d | ||
![]() |
d9bc989c74 | ||
![]() |
d1f80d4088 | ||
![]() |
4b299f0da2 | ||
![]() |
ed8006db4c | ||
![]() |
97f6c7d50a | ||
![]() |
5e3f68a180 | ||
![]() |
f73d1fc588 | ||
![]() |
aa232de426 | ||
![]() |
34ce754ef6 | ||
![]() |
5f385b2016 | ||
![]() |
da7c17271e | ||
![]() |
69fbd631ff | ||
![]() |
deb209e358 | ||
![]() |
ea6b4cbc8d | ||
![]() |
2f21a09b2e | ||
![]() |
4ab775f2c1 | ||
![]() |
b6e8023104 | ||
![]() |
4f1597e5d2 | ||
![]() |
4f81d1969e | ||
![]() |
ad3e615ac7 | ||
![]() |
e9a2301d2b | ||
![]() |
48bbfa31af | ||
![]() |
d7f5f563cc | ||
![]() |
6c29149fbe | ||
![]() |
bbd4d43317 | ||
![]() |
c4d2f62657 | ||
![]() |
5d78bf178f | ||
![]() |
58749497bd | ||
![]() |
5c6e643efb |
14
Dockerfile
14
Dockerfile
@@ -21,13 +21,15 @@ RUN apk add build-base &&\
|
|||||||
make build-dashboard
|
make build-dashboard
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
WORKDIR /root/
|
RUN adduser -D -h /authorizer -u 1000 -k /dev/null authorizer
|
||||||
|
WORKDIR /authorizer
|
||||||
RUN mkdir app dashboard
|
RUN mkdir app dashboard
|
||||||
COPY --from=node-builder /authorizer/app/build app/build
|
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/build app/build
|
||||||
COPY --from=node-builder /authorizer/app/favicon_io app/favicon_io
|
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/favicon_io app/favicon_io
|
||||||
COPY --from=node-builder /authorizer/dashboard/build dashboard/build
|
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/build dashboard/build
|
||||||
COPY --from=node-builder /authorizer/dashboard/favicon_io dashboard/favicon_io
|
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/favicon_io dashboard/favicon_io
|
||||||
COPY --from=go-builder /authorizer/build build
|
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" ]
|
||||||
|
34
README.md
34
README.md
@@ -7,19 +7,17 @@
|
|||||||
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
|
||||||
|
|
||||||
@@ -29,20 +27,22 @@
|
|||||||
- ✅ OAuth2 and OpenID compatible APIs
|
- ✅ 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 magic link login
|
- ✅ Password-less login with magic link login
|
||||||
|
- ✅ Multi factor authentication
|
||||||
|
- ✅ Email templating
|
||||||
|
- ✅ Webhooks
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- 2 Factor authentication
|
- [VueJS SDK](https://github.com/authorizerdev/authorizer-vue)
|
||||||
- VueJS SDK
|
- [Svelte SDK](https://github.com/authorizerdev/authorizer-svelte)
|
||||||
- Svelte SDK
|
- [Golang SDK](https://github.com/authorizerdev/authorizer-go)
|
||||||
- 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
|
||||||
@@ -63,11 +63,11 @@
|
|||||||
|
|
||||||
Deploy production ready Authorizer instance using one click deployment options available below
|
Deploy production ready Authorizer instance using one click deployment options available below
|
||||||
|
|
||||||
| **Infra provider** | **One-click link** | **Additional information** |
|
| **Infra provider** | **One-click link** | **Additional information** |
|
||||||
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
|
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
|
||||||
| Railway.app | <a href="https://railway.app/new/template?template=https://github.com/authorizerdev/authorizer-railway&plugins=postgresql,redis"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) |
|
| 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) |
|
| 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 | [](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
|
| Render | [](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
|
||||||
|
|
||||||
### Deploy Authorizer Using Source Code
|
### Deploy Authorizer Using Source Code
|
||||||
|
|
||||||
|
50
app/package-lock.json
generated
50
app/package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "^0.26.0-beta.0",
|
"@authorizerdev/authorizer-react": "^1.1.0",
|
||||||
"@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",
|
||||||
@@ -26,22 +26,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-js": {
|
"node_modules/@authorizerdev/authorizer-js": {
|
||||||
"version": "0.17.0-beta.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.17.0-beta.1.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.0.tgz",
|
||||||
"integrity": "sha512-jUlFUrs4Ys6LZ5hclPeRt84teygi+bA57d/IpV9GAqOrfifv70jkFeDln4+Bs0mZk74el23Xn+DR9380mqE4Cg==",
|
"integrity": "sha512-MdEw1SjhIm7pXq20AscHSbnAta2PC3w7GNBY52/OzmlBXUGH3ooUQX/aszbYOse3FlhapcrGrRvg4sNM7faGAg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-fetch": "^2.6.1"
|
"cross-fetch": "^3.1.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-react": {
|
"node_modules/@authorizerdev/authorizer-react": {
|
||||||
"version": "0.26.0-beta.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.26.0-beta.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.0.tgz",
|
||||||
"integrity": "sha512-YfyiGYBmbsp3tLWIxOrOZ/hUTCmdMXVE9SLE8m1xsFsxzJJlUhepp0AMahSbH5EyLj5bchOhOw/rzgpnDZDvMw==",
|
"integrity": "sha512-8ooyBREFI6ohHApVOPQitFr7T0w0SlpEVZruvU9oqa8OQ77UBxLQh+PCRKKPw7FeQRdCdh/VQyl17W7Xphp1NA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": "^0.17.0-beta.1",
|
"@authorizerdev/authorizer-js": "^1.1.0",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
@@ -404,6 +404,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-fetch": {
|
||||||
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "2.6.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/css-color-keywords": {
|
"node_modules/css-color-keywords": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
||||||
@@ -852,19 +860,19 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": {
|
"@authorizerdev/authorizer-js": {
|
||||||
"version": "0.17.0-beta.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.17.0-beta.1.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.0.tgz",
|
||||||
"integrity": "sha512-jUlFUrs4Ys6LZ5hclPeRt84teygi+bA57d/IpV9GAqOrfifv70jkFeDln4+Bs0mZk74el23Xn+DR9380mqE4Cg==",
|
"integrity": "sha512-MdEw1SjhIm7pXq20AscHSbnAta2PC3w7GNBY52/OzmlBXUGH3ooUQX/aszbYOse3FlhapcrGrRvg4sNM7faGAg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"node-fetch": "^2.6.1"
|
"cross-fetch": "^3.1.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@authorizerdev/authorizer-react": {
|
"@authorizerdev/authorizer-react": {
|
||||||
"version": "0.26.0-beta.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.26.0-beta.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.0.tgz",
|
||||||
"integrity": "sha512-YfyiGYBmbsp3tLWIxOrOZ/hUTCmdMXVE9SLE8m1xsFsxzJJlUhepp0AMahSbH5EyLj5bchOhOw/rzgpnDZDvMw==",
|
"integrity": "sha512-8ooyBREFI6ohHApVOPQitFr7T0w0SlpEVZruvU9oqa8OQ77UBxLQh+PCRKKPw7FeQRdCdh/VQyl17W7Xphp1NA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@authorizerdev/authorizer-js": "^0.17.0-beta.1",
|
"@authorizerdev/authorizer-js": "^1.1.0",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
@@ -1161,6 +1169,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
},
|
},
|
||||||
|
"cross-fetch": {
|
||||||
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
||||||
|
"requires": {
|
||||||
|
"node-fetch": "2.6.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"css-color-keywords": {
|
"css-color-keywords": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
"author": "Lakhan Samani",
|
"author": "Lakhan Samani",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "^0.26.0-beta.0",
|
"@authorizerdev/authorizer-react": "^1.1.0",
|
||||||
"@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",
|
||||||
|
@@ -4,6 +4,12 @@ import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
|||||||
import Root from './Root';
|
import Root from './Root';
|
||||||
import { createRandomString } from './utils/common';
|
import { createRandomString } from './utils/common';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__authorizer__: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const state = searchParams.get('state') || createRandomString();
|
const state = searchParams.get('state') || createRandomString();
|
||||||
@@ -24,7 +30,6 @@ export default function App() {
|
|||||||
urlProps.redirectURL = window.location.origin + '/app';
|
urlProps.redirectURL = window.location.origin + '/app';
|
||||||
}
|
}
|
||||||
const globalState: Record<string, string> = {
|
const globalState: Record<string, string> = {
|
||||||
// @ts-ignore
|
|
||||||
...window['__authorizer__'],
|
...window['__authorizer__'],
|
||||||
...urlProps,
|
...urlProps,
|
||||||
};
|
};
|
||||||
|
386
dashboard/package-lock.json
generated
386
dashboard/package-lock.json
generated
@@ -17,9 +17,6 @@
|
|||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-router-dom": "^5.3.2",
|
"@types/react-router-dom": "^5.3.2",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"draft-js": "^0.11.7",
|
|
||||||
"draft-js-import-html": "^1.4.1",
|
|
||||||
"draftjs-to-html": "^0.9.1",
|
|
||||||
"esbuild": "^0.14.9",
|
"esbuild": "^0.14.9",
|
||||||
"focus-visible": "^5.2.0",
|
"focus-visible": "^5.2.0",
|
||||||
"framer-motion": "^5.5.5",
|
"framer-motion": "^5.5.5",
|
||||||
@@ -29,14 +26,14 @@
|
|||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-draft-wysiwyg": "^1.15.0",
|
"react-draft-wysiwyg": "^1.15.0",
|
||||||
"react-dropzone": "^12.0.4",
|
"react-dropzone": "^12.0.4",
|
||||||
|
"react-email-editor": "^1.6.1",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-router-dom": "^6.2.1",
|
"react-router-dom": "^6.2.1",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
"urql": "^2.0.6"
|
"urql": "^2.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/draftjs-to-html": "^0.8.1",
|
"@types/react-email-editor": "^1.1.7"
|
||||||
"@types/react-draft-wysiwyg": "^1.13.4"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
@@ -1153,25 +1150,6 @@
|
|||||||
"react-dom": "^16.8.0 || 17.x"
|
"react-dom": "^16.8.0 || 17.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/draft-js": {
|
|
||||||
"version": "0.11.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/draft-js/-/draft-js-0.11.9.tgz",
|
|
||||||
"integrity": "sha512-cQJBZjjIlGaPA1tOY+wGz2KhlPtAAZOIXpUvGPxPRw5uzZ2tcj8m6Yu1QDV9YgP36+cqE3cUvgkARBzgUiuI/Q==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"immutable": "~3.7.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/draftjs-to-html": {
|
|
||||||
"version": "0.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/draftjs-to-html/-/draftjs-to-html-0.8.1.tgz",
|
|
||||||
"integrity": "sha512-NBkphQs+qZ/sAz/j1pCUaxkPAOx00LTsE88aMSSfcvK+UfCpjHJDqIMCkm6wKotuJvY5w0BtdRazQ0sAaXzPdg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/draft-js": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/history": {
|
"node_modules/@types/history": {
|
||||||
"version": "4.7.9",
|
"version": "4.7.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz",
|
||||||
@@ -1218,13 +1196,12 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/react-draft-wysiwyg": {
|
"node_modules/@types/react-email-editor": {
|
||||||
"version": "1.13.4",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-draft-wysiwyg/-/react-draft-wysiwyg-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-email-editor/-/react-email-editor-1.1.7.tgz",
|
||||||
"integrity": "sha512-wasD1t78JDmQvdPDRPf/mf5FSHMlncunW0F6KMOKB3awzi3Wi21yHMGsRAUOkfTr3R8F+yceG8fSLz0kYWu/QA==",
|
"integrity": "sha512-OURTAgaE9pjA6KiU97k13fPdoglI1ZyowUuZ0nu5tTSyrw5PiZoYzYEf9y25YTjmw/ohxT5yqoP0tt+AjSh1qQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/draft-js": "*",
|
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1296,11 +1273,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"node_modules/asap": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
|
|
||||||
},
|
|
||||||
"node_modules/attr-accept": {
|
"node_modules/attr-accept": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||||
@@ -1387,16 +1359,6 @@
|
|||||||
"toggle-selection": "^1.0.6"
|
"toggle-selection": "^1.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/core-js": {
|
|
||||||
"version": "3.24.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz",
|
|
||||||
"integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/core-js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cosmiconfig": {
|
"node_modules/cosmiconfig": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||||
@@ -1412,14 +1374,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cross-fetch": {
|
|
||||||
"version": "3.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
|
||||||
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
|
||||||
"dependencies": {
|
|
||||||
"node-fetch": "2.6.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/css-box-model": {
|
"node_modules/css-box-model": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
||||||
@@ -1448,59 +1402,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
|
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
|
||||||
},
|
},
|
||||||
"node_modules/draft-js": {
|
|
||||||
"version": "0.11.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.11.7.tgz",
|
|
||||||
"integrity": "sha512-ne7yFfN4sEL82QPQEn80xnADR8/Q6ALVworbC5UOSzOvjffmYfFsr3xSZtxbIirti14R7Y33EZC5rivpLgIbsg==",
|
|
||||||
"dependencies": {
|
|
||||||
"fbjs": "^2.0.0",
|
|
||||||
"immutable": "~3.7.4",
|
|
||||||
"object-assign": "^4.1.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=0.14.0",
|
|
||||||
"react-dom": ">=0.14.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/draft-js-import-element": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/draft-js-import-element/-/draft-js-import-element-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-WmYT5PrCm47lGL5FkH6sRO3TTAcn7qNHsD3igiPqLG/RXrqyKrqN4+wBgbcT2lhna/yfWTRtgzAbQsSJoS1Meg==",
|
|
||||||
"dependencies": {
|
|
||||||
"draft-js-utils": "^1.4.0",
|
|
||||||
"synthetic-dom": "^1.4.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"draft-js": ">=0.10.0",
|
|
||||||
"immutable": "3.x.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/draft-js-import-html": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/draft-js-import-html/-/draft-js-import-html-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-KOZmtgxZriCDgg5Smr3Y09TjubvXe7rHPy/2fuLSsL+aSzwUDwH/aHDA/k47U+WfpmL4qgyg4oZhqx9TYJV0tg==",
|
|
||||||
"dependencies": {
|
|
||||||
"draft-js-import-element": "^1.4.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"draft-js": ">=0.10.0",
|
|
||||||
"immutable": "3.x.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/draft-js-utils": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/draft-js-utils/-/draft-js-utils-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-xE81Y+z/muC5D5z9qWmKfxEW1XyXfsBzSbSBk2JRsoD0yzMGGHQm/0MtuqHl/EUDkaBJJLjJ2EACycoDMY/OOg==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"draft-js": ">=0.10.0",
|
|
||||||
"immutable": "3.x.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/draftjs-to-html": {
|
|
||||||
"version": "0.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/draftjs-to-html/-/draftjs-to-html-0.9.1.tgz",
|
|
||||||
"integrity": "sha512-fFstE6+IayaVFBEvaFt/wN8vdj8FsTRzij7dy7LI9QIwf5LgfHFi9zSpvCg+feJ2tbYVqHxUkjcibwpsTpgFVQ=="
|
|
||||||
},
|
|
||||||
"node_modules/draftjs-utils": {
|
"node_modules/draftjs-utils": {
|
||||||
"version": "0.10.2",
|
"version": "0.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz",
|
||||||
@@ -1774,26 +1675,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fbjs": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-8XA8ny9ifxrAWlyhAbexXcs3rRMtxWcs3M0lctLfB49jRDHiaxj+Mo0XxbwE7nKZYzgCFoq64FS+WFd4IycPPQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"core-js": "^3.6.4",
|
|
||||||
"cross-fetch": "^3.0.4",
|
|
||||||
"fbjs-css-vars": "^1.0.0",
|
|
||||||
"loose-envify": "^1.0.0",
|
|
||||||
"object-assign": "^4.1.0",
|
|
||||||
"promise": "^7.1.1",
|
|
||||||
"setimmediate": "^1.0.5",
|
|
||||||
"ua-parser-js": "^0.7.18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fbjs-css-vars": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="
|
|
||||||
},
|
|
||||||
"node_modules/file-selector": {
|
"node_modules/file-selector": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz",
|
||||||
@@ -1958,14 +1839,6 @@
|
|||||||
"immutable": "3.x.x || 4.x.x"
|
"immutable": "3.x.x || 4.x.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/immutable": {
|
|
||||||
"version": "3.7.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
|
|
||||||
"integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/import-fresh": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -2049,25 +1922,6 @@
|
|||||||
"loose-envify": "cli.js"
|
"loose-envify": "cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-fetch": {
|
|
||||||
"version": "2.6.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
|
||||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"whatwg-url": "^5.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "4.x || >=6.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"encoding": "^0.1.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"encoding": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -2136,14 +1990,6 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/promise": {
|
|
||||||
"version": "7.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
|
||||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
|
||||||
"dependencies": {
|
|
||||||
"asap": "~2.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/prop-types": {
|
"node_modules/prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
@@ -2224,6 +2070,14 @@
|
|||||||
"react": ">= 16.8"
|
"react": ">= 16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-email-editor": {
|
||||||
|
"version": "1.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-email-editor/-/react-email-editor-1.6.1.tgz",
|
||||||
|
"integrity": "sha512-pEWpRmTY0ok03cwTGqEOoEldnzThhuRGTrcMnv8W3/jc5MTfcr9USU/IQ9HrVvFStLKoxYBIQnSKY+iCYWOtSQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "15.x || 16.x || 17.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-fast-compare": {
|
"node_modules/react-fast-compare": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||||
@@ -2424,11 +2278,6 @@
|
|||||||
"object-assign": "^4.1.1"
|
"object-assign": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/setimmediate": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
|
|
||||||
},
|
|
||||||
"node_modules/source-map": {
|
"node_modules/source-map": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
@@ -2462,11 +2311,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/synthetic-dom": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/synthetic-dom/-/synthetic-dom-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-mHv51ZsmZ+ShT/4s5kg+MGUIhY7Ltq4v03xpN1c8T1Krb5pScsh/lzEjyhrVD0soVDbThbd2e+4dD9vnDG4rhg=="
|
|
||||||
},
|
|
||||||
"node_modules/tiny-invariant": {
|
"node_modules/tiny-invariant": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
|
||||||
@@ -2485,11 +2329,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
||||||
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
|
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
|
||||||
},
|
},
|
||||||
"node_modules/tr46": {
|
|
||||||
"version": "0.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
|
||||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
|
||||||
},
|
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
@@ -2507,24 +2346,6 @@
|
|||||||
"node": ">=4.2.0"
|
"node": ">=4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ua-parser-js": {
|
|
||||||
"version": "0.7.31",
|
|
||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
|
|
||||||
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/ua-parser-js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "paypal",
|
|
||||||
"url": "https://paypal.me/faisalman"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/uc.micro": {
|
"node_modules/uc.micro": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||||
@@ -2588,20 +2409,6 @@
|
|||||||
"loose-envify": "^1.0.0"
|
"loose-envify": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webidl-conversions": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
|
||||||
},
|
|
||||||
"node_modules/whatwg-url": {
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wonka": {
|
"node_modules/wonka": {
|
||||||
"version": "4.0.15",
|
"version": "4.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz",
|
||||||
@@ -3441,25 +3248,6 @@
|
|||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/draft-js": {
|
|
||||||
"version": "0.11.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/draft-js/-/draft-js-0.11.9.tgz",
|
|
||||||
"integrity": "sha512-cQJBZjjIlGaPA1tOY+wGz2KhlPtAAZOIXpUvGPxPRw5uzZ2tcj8m6Yu1QDV9YgP36+cqE3cUvgkARBzgUiuI/Q==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"immutable": "~3.7.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/draftjs-to-html": {
|
|
||||||
"version": "0.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/draftjs-to-html/-/draftjs-to-html-0.8.1.tgz",
|
|
||||||
"integrity": "sha512-NBkphQs+qZ/sAz/j1pCUaxkPAOx00LTsE88aMSSfcvK+UfCpjHJDqIMCkm6wKotuJvY5w0BtdRazQ0sAaXzPdg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/draft-js": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/history": {
|
"@types/history": {
|
||||||
"version": "4.7.9",
|
"version": "4.7.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz",
|
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz",
|
||||||
@@ -3506,13 +3294,12 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/react-draft-wysiwyg": {
|
"@types/react-email-editor": {
|
||||||
"version": "1.13.4",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-draft-wysiwyg/-/react-draft-wysiwyg-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-email-editor/-/react-email-editor-1.1.7.tgz",
|
||||||
"integrity": "sha512-wasD1t78JDmQvdPDRPf/mf5FSHMlncunW0F6KMOKB3awzi3Wi21yHMGsRAUOkfTr3R8F+yceG8fSLz0kYWu/QA==",
|
"integrity": "sha512-OURTAgaE9pjA6KiU97k13fPdoglI1ZyowUuZ0nu5tTSyrw5PiZoYzYEf9y25YTjmw/ohxT5yqoP0tt+AjSh1qQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/draft-js": "*",
|
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3577,11 +3364,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"asap": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
|
|
||||||
},
|
|
||||||
"attr-accept": {
|
"attr-accept": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||||
@@ -3658,11 +3440,6 @@
|
|||||||
"toggle-selection": "^1.0.6"
|
"toggle-selection": "^1.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core-js": {
|
|
||||||
"version": "3.24.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz",
|
|
||||||
"integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg=="
|
|
||||||
},
|
|
||||||
"cosmiconfig": {
|
"cosmiconfig": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||||
@@ -3675,14 +3452,6 @@
|
|||||||
"yaml": "^1.7.2"
|
"yaml": "^1.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cross-fetch": {
|
|
||||||
"version": "3.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
|
|
||||||
"integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
|
|
||||||
"requires": {
|
|
||||||
"node-fetch": "2.6.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"css-box-model": {
|
"css-box-model": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
||||||
@@ -3711,44 +3480,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
|
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
|
||||||
},
|
},
|
||||||
"draft-js": {
|
|
||||||
"version": "0.11.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.11.7.tgz",
|
|
||||||
"integrity": "sha512-ne7yFfN4sEL82QPQEn80xnADR8/Q6ALVworbC5UOSzOvjffmYfFsr3xSZtxbIirti14R7Y33EZC5rivpLgIbsg==",
|
|
||||||
"requires": {
|
|
||||||
"fbjs": "^2.0.0",
|
|
||||||
"immutable": "~3.7.4",
|
|
||||||
"object-assign": "^4.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draft-js-import-element": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/draft-js-import-element/-/draft-js-import-element-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-WmYT5PrCm47lGL5FkH6sRO3TTAcn7qNHsD3igiPqLG/RXrqyKrqN4+wBgbcT2lhna/yfWTRtgzAbQsSJoS1Meg==",
|
|
||||||
"requires": {
|
|
||||||
"draft-js-utils": "^1.4.0",
|
|
||||||
"synthetic-dom": "^1.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draft-js-import-html": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/draft-js-import-html/-/draft-js-import-html-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-KOZmtgxZriCDgg5Smr3Y09TjubvXe7rHPy/2fuLSsL+aSzwUDwH/aHDA/k47U+WfpmL4qgyg4oZhqx9TYJV0tg==",
|
|
||||||
"requires": {
|
|
||||||
"draft-js-import-element": "^1.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"draft-js-utils": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/draft-js-utils/-/draft-js-utils-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-xE81Y+z/muC5D5z9qWmKfxEW1XyXfsBzSbSBk2JRsoD0yzMGGHQm/0MtuqHl/EUDkaBJJLjJ2EACycoDMY/OOg==",
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"draftjs-to-html": {
|
|
||||||
"version": "0.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/draftjs-to-html/-/draftjs-to-html-0.9.1.tgz",
|
|
||||||
"integrity": "sha512-fFstE6+IayaVFBEvaFt/wN8vdj8FsTRzij7dy7LI9QIwf5LgfHFi9zSpvCg+feJ2tbYVqHxUkjcibwpsTpgFVQ=="
|
|
||||||
},
|
|
||||||
"draftjs-utils": {
|
"draftjs-utils": {
|
||||||
"version": "0.10.2",
|
"version": "0.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/draftjs-utils/-/draftjs-utils-0.10.2.tgz",
|
||||||
@@ -3901,26 +3632,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||||
},
|
},
|
||||||
"fbjs": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-8XA8ny9ifxrAWlyhAbexXcs3rRMtxWcs3M0lctLfB49jRDHiaxj+Mo0XxbwE7nKZYzgCFoq64FS+WFd4IycPPQ==",
|
|
||||||
"requires": {
|
|
||||||
"core-js": "^3.6.4",
|
|
||||||
"cross-fetch": "^3.0.4",
|
|
||||||
"fbjs-css-vars": "^1.0.0",
|
|
||||||
"loose-envify": "^1.0.0",
|
|
||||||
"object-assign": "^4.1.0",
|
|
||||||
"promise": "^7.1.1",
|
|
||||||
"setimmediate": "^1.0.5",
|
|
||||||
"ua-parser-js": "^0.7.18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fbjs-css-vars": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ=="
|
|
||||||
},
|
|
||||||
"file-selector": {
|
"file-selector": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz",
|
||||||
@@ -4050,11 +3761,6 @@
|
|||||||
"integrity": "sha512-kggLXBNciKDwKf+KYsuE+V5gw4dZ7nHyGMX9m0wy7urzWjKGWyNFetmArRLvRV0VrxKN70WylFsJvMTJx02OBQ==",
|
"integrity": "sha512-kggLXBNciKDwKf+KYsuE+V5gw4dZ7nHyGMX9m0wy7urzWjKGWyNFetmArRLvRV0VrxKN70WylFsJvMTJx02OBQ==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"immutable": {
|
|
||||||
"version": "3.7.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
|
|
||||||
"integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw=="
|
|
||||||
},
|
|
||||||
"import-fresh": {
|
"import-fresh": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||||
@@ -4126,14 +3832,6 @@
|
|||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-fetch": {
|
|
||||||
"version": "2.6.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
|
||||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
|
||||||
"requires": {
|
|
||||||
"whatwg-url": "^5.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@@ -4189,14 +3887,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"promise": {
|
|
||||||
"version": "7.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
|
|
||||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
|
||||||
"requires": {
|
|
||||||
"asap": "~2.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"prop-types": {
|
"prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
@@ -4256,6 +3946,12 @@
|
|||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-email-editor": {
|
||||||
|
"version": "1.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-email-editor/-/react-email-editor-1.6.1.tgz",
|
||||||
|
"integrity": "sha512-pEWpRmTY0ok03cwTGqEOoEldnzThhuRGTrcMnv8W3/jc5MTfcr9USU/IQ9HrVvFStLKoxYBIQnSKY+iCYWOtSQ==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"react-fast-compare": {
|
"react-fast-compare": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||||
@@ -4400,11 +4096,6 @@
|
|||||||
"object-assign": "^4.1.1"
|
"object-assign": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setimmediate": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
|
|
||||||
},
|
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||||
@@ -4432,11 +4123,6 @@
|
|||||||
"has-flag": "^3.0.0"
|
"has-flag": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"synthetic-dom": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/synthetic-dom/-/synthetic-dom-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-mHv51ZsmZ+ShT/4s5kg+MGUIhY7Ltq4v03xpN1c8T1Krb5pScsh/lzEjyhrVD0soVDbThbd2e+4dD9vnDG4rhg=="
|
|
||||||
},
|
|
||||||
"tiny-invariant": {
|
"tiny-invariant": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
|
||||||
@@ -4452,11 +4138,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
||||||
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
|
"integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
|
||||||
},
|
},
|
||||||
"tr46": {
|
|
||||||
"version": "0.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
|
||||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
|
||||||
},
|
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
@@ -4467,11 +4148,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
|
||||||
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg=="
|
"integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg=="
|
||||||
},
|
},
|
||||||
"ua-parser-js": {
|
|
||||||
"version": "0.7.31",
|
|
||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
|
|
||||||
"integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
|
|
||||||
},
|
|
||||||
"uc.micro": {
|
"uc.micro": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
|
||||||
@@ -4516,20 +4192,6 @@
|
|||||||
"loose-envify": "^1.0.0"
|
"loose-envify": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webidl-conversions": {
|
|
||||||
"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": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
|
||||||
"requires": {
|
|
||||||
"tr46": "~0.0.3",
|
|
||||||
"webidl-conversions": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"wonka": {
|
"wonka": {
|
||||||
"version": "4.0.15",
|
"version": "4.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz",
|
||||||
|
@@ -19,9 +19,6 @@
|
|||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
"@types/react-router-dom": "^5.3.2",
|
"@types/react-router-dom": "^5.3.2",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"draft-js": "^0.11.7",
|
|
||||||
"draft-js-import-html": "^1.4.1",
|
|
||||||
"draftjs-to-html": "^0.9.1",
|
|
||||||
"esbuild": "^0.14.9",
|
"esbuild": "^0.14.9",
|
||||||
"focus-visible": "^5.2.0",
|
"focus-visible": "^5.2.0",
|
||||||
"framer-motion": "^5.5.5",
|
"framer-motion": "^5.5.5",
|
||||||
@@ -31,13 +28,13 @@
|
|||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-draft-wysiwyg": "^1.15.0",
|
"react-draft-wysiwyg": "^1.15.0",
|
||||||
"react-dropzone": "^12.0.4",
|
"react-dropzone": "^12.0.4",
|
||||||
|
"react-email-editor": "^1.6.1",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^4.3.1",
|
||||||
"react-router-dom": "^6.2.1",
|
"react-router-dom": "^6.2.1",
|
||||||
"typescript": "^4.5.4",
|
"typescript": "^4.5.4",
|
||||||
"urql": "^2.0.6"
|
"urql": "^2.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/draftjs-to-html": "^0.8.1",
|
"@types/react-email-editor": "^1.1.7"
|
||||||
"@types/react-draft-wysiwyg": "^1.13.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Flex, Stack, Text } from '@chakra-ui/react';
|
import { Divider, Flex, Stack, Text } from '@chakra-ui/react';
|
||||||
import InputField from '../InputField';
|
import InputField from '../InputField';
|
||||||
import { SwitchInputType } from '../../constants';
|
import { SwitchInputType } from '../../constants';
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ const Features = ({ variables, setVariables }: any) => {
|
|||||||
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
|
||||||
Disable Features
|
Disable Features
|
||||||
</Text>
|
</Text>
|
||||||
<Stack spacing={6} padding="2% 0%">
|
<Stack spacing={6}>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Disable Login Page:</Text>
|
<Text fontSize="sm">Disable Login Page:</Text>
|
||||||
@@ -83,6 +83,48 @@ const Features = ({ variables, setVariables }: any) => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Flex w="100%" alignItems="baseline" flexDir="column">
|
||||||
|
<Text fontSize="sm">
|
||||||
|
Disable 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}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
<Divider paddingY={5} />
|
||||||
|
<Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
|
||||||
|
Enable Features
|
||||||
|
</Text>
|
||||||
|
<Stack spacing={6}>
|
||||||
|
<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>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -1,154 +1,201 @@
|
|||||||
import React from "react";
|
import React from 'react';
|
||||||
import { Flex, Stack, Center, Text, useMediaQuery } from "@chakra-ui/react";
|
|
||||||
import {
|
import {
|
||||||
HiddenInputType,
|
Flex,
|
||||||
TextInputType,
|
Stack,
|
||||||
TextAreaInputType,
|
Center,
|
||||||
} from "../../constants";
|
Text,
|
||||||
import GenerateKeysModal from "../GenerateKeysModal";
|
useMediaQuery,
|
||||||
import InputField from "../InputField";
|
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 = ({
|
const JSTConfigurations = ({
|
||||||
variables,
|
variables,
|
||||||
setVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
SelectInputType,
|
SelectInputType,
|
||||||
getData,
|
getData,
|
||||||
HMACEncryptionType,
|
HMACEncryptionType,
|
||||||
RSAEncryptionType,
|
RSAEncryptionType,
|
||||||
ECDSAEncryptionType,
|
ECDSAEncryptionType,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const [isNotSmallerScreen] = useMediaQuery("(min-width:600px)");
|
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
return (
|
const copyJSON = async () => {
|
||||||
<div>
|
try {
|
||||||
{" "}
|
await copyTextToClipboard(
|
||||||
<Flex
|
JSON.stringify({
|
||||||
borderRadius={5}
|
type: variables.JWT_TYPE,
|
||||||
width="100%"
|
key: variables.JWT_PUBLIC_KEY || variables.JWT_SECRET,
|
||||||
justifyContent="space-between"
|
})
|
||||||
alignItems="center"
|
);
|
||||||
paddingTop="2%"
|
toast({
|
||||||
>
|
title: `JWT config copied successfully`,
|
||||||
<Text
|
isClosable: true,
|
||||||
fontSize={isNotSmallerScreen ? "md" : "sm"}
|
status: 'success',
|
||||||
fontWeight="bold"
|
position: 'bottom-right',
|
||||||
mb={5}
|
});
|
||||||
>
|
} catch (err) {
|
||||||
JWT (JSON Web Tokens) Configurations
|
console.error({
|
||||||
</Text>
|
message: `Failed to copy JWT config`,
|
||||||
<Flex mb={7}>
|
error: err,
|
||||||
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
|
});
|
||||||
</Flex>
|
toast({
|
||||||
</Flex>
|
title: `Failed to copy JWT config`,
|
||||||
<Stack spacing={6} padding="2% 0%">
|
isClosable: true,
|
||||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
status: 'error',
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
position: 'bottom-right',
|
||||||
<Text fontSize="sm">JWT Type:</Text>
|
});
|
||||||
</Flex>
|
}
|
||||||
<Flex
|
};
|
||||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
|
||||||
mt={isNotSmallerScreen ? "0" : "2"}
|
return (
|
||||||
>
|
<div>
|
||||||
<InputField
|
{' '}
|
||||||
borderRadius={5}
|
<Flex
|
||||||
variables={variables}
|
borderRadius={5}
|
||||||
setVariables={setVariables}
|
width="100%"
|
||||||
inputType={SelectInputType}
|
justifyContent="space-between"
|
||||||
value={SelectInputType}
|
alignItems="center"
|
||||||
options={{
|
paddingTop="2%"
|
||||||
...HMACEncryptionType,
|
>
|
||||||
...RSAEncryptionType,
|
<Text
|
||||||
...ECDSAEncryptionType,
|
fontSize={isNotSmallerScreen ? 'md' : 'sm'}
|
||||||
}}
|
fontWeight="bold"
|
||||||
/>
|
mb={5}
|
||||||
</Flex>
|
>
|
||||||
</Flex>
|
JWT (JSON Web Tokens) Configurations
|
||||||
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
|
</Text>
|
||||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
<Flex mb={7}>
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
<Button
|
||||||
<Text fontSize="sm">JWT Secret</Text>
|
colorScheme="blue"
|
||||||
</Flex>
|
h="1.75rem"
|
||||||
<Center
|
size="sm"
|
||||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
variant="ghost"
|
||||||
mt={isNotSmallerScreen ? "0" : "2"}
|
onClick={copyJSON}
|
||||||
>
|
>
|
||||||
<InputField
|
Copy As JSON Config
|
||||||
borderRadius={5}
|
</Button>
|
||||||
variables={variables}
|
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
|
||||||
setVariables={setVariables}
|
</Flex>
|
||||||
fieldVisibility={fieldVisibility}
|
</Flex>
|
||||||
setFieldVisibility={setFieldVisibility}
|
<Stack spacing={6} padding="2% 0%">
|
||||||
inputType={HiddenInputType.JWT_SECRET}
|
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||||
/>
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
</Center>
|
<Text fontSize="sm">JWT Type:</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
<Flex
|
||||||
<>
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
mt={isNotSmallerScreen ? '0' : '2'}
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
>
|
||||||
<Text fontSize="sm">Public Key</Text>
|
<InputField
|
||||||
</Flex>
|
borderRadius={5}
|
||||||
<Center
|
variables={variables}
|
||||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
setVariables={setVariables}
|
||||||
mt={isNotSmallerScreen ? "0" : "2"}
|
inputType={SelectInputType}
|
||||||
>
|
value={SelectInputType}
|
||||||
<InputField
|
options={{
|
||||||
borderRadius={5}
|
...HMACEncryptionType,
|
||||||
variables={variables}
|
...RSAEncryptionType,
|
||||||
setVariables={setVariables}
|
...ECDSAEncryptionType,
|
||||||
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
|
}}
|
||||||
placeholder="Add public key here"
|
/>
|
||||||
minH="25vh"
|
</Flex>
|
||||||
/>
|
</Flex>
|
||||||
</Center>
|
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
|
||||||
</Flex>
|
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
<Flex w="30%" justifyContent="start" alignItems="center">
|
<Text fontSize="sm">JWT Secret</Text>
|
||||||
<Text fontSize="sm">Private Key</Text>
|
</Flex>
|
||||||
</Flex>
|
<Center
|
||||||
<Center
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
mt={isNotSmallerScreen ? '0' : '2'}
|
||||||
mt={isNotSmallerScreen ? "0" : "2"}
|
>
|
||||||
>
|
<InputField
|
||||||
<InputField
|
borderRadius={5}
|
||||||
borderRadius={5}
|
variables={variables}
|
||||||
variables={variables}
|
setVariables={setVariables}
|
||||||
setVariables={setVariables}
|
fieldVisibility={fieldVisibility}
|
||||||
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
|
setFieldVisibility={setFieldVisibility}
|
||||||
placeholder="Add private key here"
|
inputType={HiddenInputType.JWT_SECRET}
|
||||||
minH="25vh"
|
/>
|
||||||
/>
|
</Center>
|
||||||
</Center>
|
</Flex>
|
||||||
</Flex>
|
) : (
|
||||||
</>
|
<>
|
||||||
)}
|
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||||
<Flex direction={isNotSmallerScreen ? "row" : "column"}>
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
<Flex
|
<Text fontSize="sm">Public Key</Text>
|
||||||
w={isNotSmallerScreen ? "30%" : "40%"}
|
</Flex>
|
||||||
justifyContent="start"
|
<Center
|
||||||
alignItems="center"
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
>
|
mt={isNotSmallerScreen ? '0' : '2'}
|
||||||
<Text fontSize="sm" orientation="vertical">
|
>
|
||||||
JWT Role Claim:
|
<InputField
|
||||||
</Text>
|
borderRadius={5}
|
||||||
</Flex>
|
variables={variables}
|
||||||
<Center
|
setVariables={setVariables}
|
||||||
w={isNotSmallerScreen ? "70%" : "100%"}
|
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
|
||||||
mt={isNotSmallerScreen ? "0" : "2"}
|
placeholder="Add public key here"
|
||||||
>
|
minH="25vh"
|
||||||
<InputField
|
/>
|
||||||
borderRadius={5}
|
</Center>
|
||||||
variables={variables}
|
</Flex>
|
||||||
setVariables={setVariables}
|
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||||
inputType={TextInputType.JWT_ROLE_CLAIM}
|
<Flex w="30%" justifyContent="start" alignItems="center">
|
||||||
/>
|
<Text fontSize="sm">Private Key</Text>
|
||||||
</Center>
|
</Flex>
|
||||||
</Flex>
|
<Center
|
||||||
</Stack>
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
</div>
|
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;
|
export default JSTConfigurations;
|
||||||
|
@@ -15,6 +15,7 @@ import {
|
|||||||
FaFacebookF,
|
FaFacebookF,
|
||||||
FaLinkedin,
|
FaLinkedin,
|
||||||
FaApple,
|
FaApple,
|
||||||
|
FaTwitter,
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import { TextInputType, HiddenInputType } from '../../constants';
|
import { TextInputType, HiddenInputType } from '../../constants';
|
||||||
|
|
||||||
@@ -264,6 +265,44 @@ const OAuthConfig = ({
|
|||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</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>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
@@ -17,13 +17,22 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
useToast,
|
useToast,
|
||||||
|
Alert,
|
||||||
|
AlertIcon,
|
||||||
|
Collapse,
|
||||||
|
Box,
|
||||||
|
TableContainer,
|
||||||
|
Table,
|
||||||
|
Thead,
|
||||||
|
Tr,
|
||||||
|
Th,
|
||||||
|
Tbody,
|
||||||
|
Td,
|
||||||
|
Code,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { FaPlus } from 'react-icons/fa';
|
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
|
||||||
import { useClient } from 'urql';
|
import { useClient } from 'urql';
|
||||||
import { Editor } from 'react-draft-wysiwyg';
|
import EmailEditor from 'react-email-editor';
|
||||||
import { EditorState, convertToRaw, Modifier } from 'draft-js';
|
|
||||||
import draftToHtml from 'draftjs-to-html';
|
|
||||||
import { stateFromHTML } from 'draft-js-import-html';
|
|
||||||
import {
|
import {
|
||||||
UpdateModalViews,
|
UpdateModalViews,
|
||||||
EmailTemplateInputDataFields,
|
EmailTemplateInputDataFields,
|
||||||
@@ -39,6 +48,7 @@ interface selectedEmailTemplateDataTypes {
|
|||||||
[EmailTemplateInputDataFields.SUBJECT]: string;
|
[EmailTemplateInputDataFields.SUBJECT]: string;
|
||||||
[EmailTemplateInputDataFields.CREATED_AT]: number;
|
[EmailTemplateInputDataFields.CREATED_AT]: number;
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
||||||
|
[EmailTemplateInputDataFields.DESIGN]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateEmailTemplateInputPropTypes {
|
interface UpdateEmailTemplateInputPropTypes {
|
||||||
@@ -50,6 +60,7 @@ interface UpdateEmailTemplateInputPropTypes {
|
|||||||
interface templateVariableDataTypes {
|
interface templateVariableDataTypes {
|
||||||
text: string;
|
text: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface emailTemplateDataType {
|
interface emailTemplateDataType {
|
||||||
@@ -77,11 +88,9 @@ const UpdateEmailTemplate = ({
|
|||||||
}: UpdateEmailTemplateInputPropTypes) => {
|
}: UpdateEmailTemplateInputPropTypes) => {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const emailEditorRef = useRef(null);
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [editorState, setEditorState] = React.useState<EditorState>(
|
|
||||||
EditorState.createEmpty()
|
|
||||||
);
|
|
||||||
const [templateVariables, setTemplateVariables] = useState<
|
const [templateVariables, setTemplateVariables] = useState<
|
||||||
templateVariableDataTypes[]
|
templateVariableDataTypes[]
|
||||||
>([]);
|
>([]);
|
||||||
@@ -91,9 +100,23 @@ const UpdateEmailTemplate = ({
|
|||||||
const [validator, setValidator] = useState<validatorDataType>({
|
const [validator, setValidator] = useState<validatorDataType>({
|
||||||
...initTemplateValidatorData,
|
...initTemplateValidatorData,
|
||||||
});
|
});
|
||||||
const onEditorStateChange = (editorState: EditorState) => {
|
const [isDynamicVariableInfoOpen, setIsDynamicVariableInfoOpen] =
|
||||||
setEditorState(editorState);
|
useState<boolean>(false);
|
||||||
|
|
||||||
|
const onReady = () => {
|
||||||
|
if (selectedTemplate) {
|
||||||
|
const { design } = selectedTemplate;
|
||||||
|
try {
|
||||||
|
const designData = JSON.parse(design);
|
||||||
|
// @ts-ignore
|
||||||
|
emailEditorRef.current.editor.loadDesign(designData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const inputChangehandler = (inputType: string, value: any) => {
|
const inputChangehandler = (inputType: string, value: any) => {
|
||||||
if (inputType !== EmailTemplateInputDataFields.EVENT_NAME) {
|
if (inputType !== EmailTemplateInputDataFields.EVENT_NAME) {
|
||||||
setValidator({
|
setValidator({
|
||||||
@@ -105,14 +128,8 @@ const UpdateEmailTemplate = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const validateData = () => {
|
const validateData = () => {
|
||||||
const rawData: string = draftToHtml(
|
|
||||||
convertToRaw(editorState.getCurrentContent())
|
|
||||||
).trim();
|
|
||||||
return (
|
return (
|
||||||
!loading &&
|
!loading &&
|
||||||
rawData &&
|
|
||||||
rawData !== '<p></p>' &&
|
|
||||||
rawData !== '<h1></h1>' &&
|
|
||||||
templateData[EmailTemplateInputDataFields.EVENT_NAME].length > 0 &&
|
templateData[EmailTemplateInputDataFields.EVENT_NAME].length > 0 &&
|
||||||
templateData[EmailTemplateInputDataFields.SUBJECT].length > 0 &&
|
templateData[EmailTemplateInputDataFields.SUBJECT].length > 0 &&
|
||||||
validator[EmailTemplateInputDataFields.SUBJECT]
|
validator[EmailTemplateInputDataFields.SUBJECT]
|
||||||
@@ -122,69 +139,72 @@ const UpdateEmailTemplate = ({
|
|||||||
const saveData = async () => {
|
const saveData = async () => {
|
||||||
if (!validateData()) return;
|
if (!validateData()) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const params = {
|
// @ts-ignore
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]:
|
return await emailEditorRef.current.editor.exportHtml(async (data) => {
|
||||||
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
const { design, html } = data;
|
||||||
[EmailTemplateInputDataFields.SUBJECT]:
|
if (!html || !design) {
|
||||||
templateData[EmailTemplateInputDataFields.SUBJECT],
|
setLoading(false);
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: draftToHtml(
|
return;
|
||||||
convertToRaw(editorState.getCurrentContent())
|
}
|
||||||
).trim(),
|
const params = {
|
||||||
};
|
[EmailTemplateInputDataFields.EVENT_NAME]:
|
||||||
let res: any = {};
|
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
||||||
if (
|
[EmailTemplateInputDataFields.SUBJECT]:
|
||||||
view === UpdateModalViews.Edit &&
|
templateData[EmailTemplateInputDataFields.SUBJECT],
|
||||||
selectedTemplate?.[EmailTemplateInputDataFields.ID]
|
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
|
||||||
) {
|
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
|
||||||
res = await client
|
};
|
||||||
.mutation(EditEmailTemplate, {
|
let res: any = {};
|
||||||
params: {
|
if (
|
||||||
...params,
|
view === UpdateModalViews.Edit &&
|
||||||
id: selectedTemplate[EmailTemplateInputDataFields.ID],
|
selectedTemplate?.[EmailTemplateInputDataFields.ID]
|
||||||
},
|
) {
|
||||||
})
|
res = await client
|
||||||
.toPromise();
|
.mutation(EditEmailTemplate, {
|
||||||
} else {
|
params: {
|
||||||
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
|
...params,
|
||||||
}
|
id: selectedTemplate[EmailTemplateInputDataFields.ID],
|
||||||
setLoading(false);
|
},
|
||||||
if (res.error) {
|
})
|
||||||
toast({
|
.toPromise();
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
} else {
|
||||||
isClosable: true,
|
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
|
||||||
status: 'error',
|
}
|
||||||
position: 'bottom-right',
|
setLoading(false);
|
||||||
});
|
if (res.error) {
|
||||||
} else if (
|
toast({
|
||||||
res.data?._add_email_template ||
|
title: capitalizeFirstLetter(res.error.message),
|
||||||
res.data?._update_email_template
|
isClosable: true,
|
||||||
) {
|
status: 'error',
|
||||||
toast({
|
position: 'bottom-right',
|
||||||
title: capitalizeFirstLetter(
|
});
|
||||||
res.data?._add_email_template?.message ||
|
} else if (
|
||||||
res.data?._update_email_template?.message
|
res.data?._add_email_template ||
|
||||||
),
|
res.data?._update_email_template
|
||||||
isClosable: true,
|
) {
|
||||||
status: 'success',
|
toast({
|
||||||
position: 'bottom-right',
|
title: capitalizeFirstLetter(
|
||||||
});
|
res.data?._add_email_template?.message ||
|
||||||
setTemplateData({
|
res.data?._update_email_template?.message
|
||||||
...initTemplateData,
|
),
|
||||||
});
|
isClosable: true,
|
||||||
setValidator({ ...initTemplateValidatorData });
|
status: 'success',
|
||||||
fetchEmailTemplatesData();
|
position: 'bottom-right',
|
||||||
}
|
});
|
||||||
view === UpdateModalViews.ADD && onClose();
|
setTemplateData({
|
||||||
|
...initTemplateData,
|
||||||
|
});
|
||||||
|
setValidator({ ...initTemplateValidatorData });
|
||||||
|
fetchEmailTemplatesData();
|
||||||
|
}
|
||||||
|
view === UpdateModalViews.ADD && onClose();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
const resetData = () => {
|
const resetData = () => {
|
||||||
if (selectedTemplate) {
|
if (selectedTemplate) {
|
||||||
setTemplateData(selectedTemplate);
|
setTemplateData(selectedTemplate);
|
||||||
setEditorState(
|
|
||||||
EditorState.createWithContent(stateFromHTML(selectedTemplate.template))
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
setTemplateData({ ...initTemplateData });
|
setTemplateData({ ...initTemplateData });
|
||||||
setEditorState(EditorState.createEmpty());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -194,35 +214,36 @@ const UpdateEmailTemplate = ({
|
|||||||
selectedTemplate &&
|
selectedTemplate &&
|
||||||
Object.keys(selectedTemplate || {}).length
|
Object.keys(selectedTemplate || {}).length
|
||||||
) {
|
) {
|
||||||
const { id, created_at, template, ...rest } = selectedTemplate;
|
const { id, created_at, template, design, ...rest } = selectedTemplate;
|
||||||
setTemplateData(rest);
|
setTemplateData(rest);
|
||||||
setEditorState(EditorState.createWithContent(stateFromHTML(template)));
|
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updatedTemplateVariables = Object.entries(
|
const updatedTemplateVariables = Object.entries(
|
||||||
emailTemplateVariables
|
emailTemplateVariables
|
||||||
).reduce((acc, varData): any => {
|
).reduce((acc, [key, val]): any => {
|
||||||
if (
|
if (
|
||||||
(templateData[EmailTemplateInputDataFields.EVENT_NAME] !==
|
(templateData[EmailTemplateInputDataFields.EVENT_NAME] !==
|
||||||
emailTemplateEventNames['Verify Otp'] &&
|
emailTemplateEventNames['Verify Otp'] &&
|
||||||
varData[1] === emailTemplateVariables.otp) ||
|
val === emailTemplateVariables.otp) ||
|
||||||
(templateData[EmailTemplateInputDataFields.EVENT_NAME] ===
|
(templateData[EmailTemplateInputDataFields.EVENT_NAME] ===
|
||||||
emailTemplateEventNames['Verify Otp'] &&
|
emailTemplateEventNames['Verify Otp'] &&
|
||||||
varData[1] === emailTemplateVariables.verification_url)
|
val === emailTemplateVariables.verification_url)
|
||||||
) {
|
) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
...acc,
|
...acc,
|
||||||
{
|
{
|
||||||
text: varData[0],
|
text: key,
|
||||||
value: varData[1],
|
value: val.value,
|
||||||
|
description: val.description,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
setTemplateVariables(updatedTemplateVariables);
|
setTemplateVariables(updatedTemplateVariables);
|
||||||
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
|
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{view === UpdateModalViews.ADD ? (
|
{view === UpdateModalViews.ADD ? (
|
||||||
@@ -239,7 +260,14 @@ const UpdateEmailTemplate = ({
|
|||||||
) : (
|
) : (
|
||||||
<MenuItem onClick={onOpen}>Edit</MenuItem>
|
<MenuItem onClick={onOpen}>Edit</MenuItem>
|
||||||
)}
|
)}
|
||||||
<Modal isOpen={isOpen} onClose={onClose} size="3xl">
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={() => {
|
||||||
|
resetData();
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
size="6xl"
|
||||||
|
>
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
@@ -256,6 +284,73 @@ const UpdateEmailTemplate = ({
|
|||||||
borderColor="gray.200"
|
borderColor="gray.200"
|
||||||
p="5"
|
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
|
<Flex
|
||||||
width="100%"
|
width="100%"
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
@@ -290,7 +385,7 @@ const UpdateEmailTemplate = ({
|
|||||||
width="100%"
|
width="100%"
|
||||||
justifyContent="start"
|
justifyContent="start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
marginBottom="5%"
|
marginBottom="2%"
|
||||||
>
|
>
|
||||||
<Flex flex="1">Subject</Flex>
|
<Flex flex="1">Subject</Flex>
|
||||||
<Flex flex="3">
|
<Flex flex="3">
|
||||||
@@ -315,33 +410,21 @@ const UpdateEmailTemplate = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
<Flex
|
||||||
width="100%"
|
width="100%"
|
||||||
justifyContent="space-between"
|
justifyContent="flex-start"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
marginBottom="2%"
|
marginBottom="2%"
|
||||||
>
|
>
|
||||||
<Flex>Template Body</Flex>
|
Template Body
|
||||||
<Text
|
</Flex>
|
||||||
style={{
|
<Flex
|
||||||
fontSize: 14,
|
width="100%"
|
||||||
}}
|
justifyContent="flex-start"
|
||||||
color="gray.400"
|
alignItems="center"
|
||||||
>{`To select dynamic variables open curly braces "{"`}</Text>
|
border="1px solid"
|
||||||
|
borderColor="gray.200"
|
||||||
|
>
|
||||||
|
<EmailEditor ref={emailEditorRef} onReady={onReady} />
|
||||||
</Flex>
|
</Flex>
|
||||||
<Editor
|
|
||||||
editorState={editorState}
|
|
||||||
onEditorStateChange={onEditorStateChange}
|
|
||||||
editorStyle={{
|
|
||||||
border: '1px solid #d9d9d9',
|
|
||||||
borderRadius: '5px',
|
|
||||||
marginTop: '2%',
|
|
||||||
height: '35vh',
|
|
||||||
}}
|
|
||||||
mention={{
|
|
||||||
separator: ' ',
|
|
||||||
trigger: '{',
|
|
||||||
suggestions: templateVariables,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@@ -21,6 +21,9 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
useToast,
|
useToast,
|
||||||
|
Alert,
|
||||||
|
AlertIcon,
|
||||||
|
Divider,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FaAngleDown,
|
FaAngleDown,
|
||||||
@@ -468,68 +471,12 @@ const UpdateWebhookModal = ({
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
marginBottom="5%"
|
|
||||||
flexDirection="column"
|
|
||||||
>
|
|
||||||
<Flex
|
|
||||||
width="100%"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Flex>
|
|
||||||
Payload
|
|
||||||
<Text color="gray.500" ml={1}>
|
|
||||||
(example)
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Button
|
|
||||||
onClick={() => setIsShowingPayload(!isShowingPayload)}
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
{isShowingPayload ? <FaAngleUp /> : <FaAngleDown />}
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
<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>
|
|
||||||
<Flex
|
<Flex
|
||||||
width="100%"
|
width="100%"
|
||||||
justifyContent="space-between"
|
justifyContent="space-between"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
marginBottom="2%"
|
marginBottom="5%"
|
||||||
>
|
>
|
||||||
<Flex>Headers</Flex>
|
<Flex>Headers</Flex>
|
||||||
<Flex>
|
<Flex>
|
||||||
@@ -546,7 +493,8 @@ const UpdateWebhookModal = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex flexDirection="column" maxH={220} overflowY="scroll">
|
|
||||||
|
<Flex flexDirection="column" maxH={220} overflowY="auto">
|
||||||
{webhook[WebhookInputDataFields.HEADERS]?.map(
|
{webhook[WebhookInputDataFields.HEADERS]?.map(
|
||||||
(headerData, index) => (
|
(headerData, index) => (
|
||||||
<Flex
|
<Flex
|
||||||
@@ -615,6 +563,54 @@ const UpdateWebhookModal = ({
|
|||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</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>
|
</Flex>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@@ -9,6 +9,7 @@ export const TextInputType = {
|
|||||||
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
|
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
|
||||||
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
|
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
|
||||||
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
|
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
|
||||||
|
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
|
||||||
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
|
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
|
||||||
REDIS_URL: 'REDIS_URL',
|
REDIS_URL: 'REDIS_URL',
|
||||||
SMTP_HOST: 'SMTP_HOST',
|
SMTP_HOST: 'SMTP_HOST',
|
||||||
@@ -35,6 +36,7 @@ export const HiddenInputType = {
|
|||||||
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
|
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
|
||||||
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
|
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
|
||||||
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
|
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
|
||||||
|
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
|
||||||
JWT_SECRET: 'JWT_SECRET',
|
JWT_SECRET: 'JWT_SECRET',
|
||||||
SMTP_PASSWORD: 'SMTP_PASSWORD',
|
SMTP_PASSWORD: 'SMTP_PASSWORD',
|
||||||
ADMIN_SECRET: 'ADMIN_SECRET',
|
ADMIN_SECRET: 'ADMIN_SECRET',
|
||||||
@@ -68,6 +70,8 @@ export const SwitchInputType = {
|
|||||||
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
|
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
|
||||||
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
|
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
|
||||||
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
|
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
|
||||||
|
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
|
||||||
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateInputType = {
|
export const DateInputType = {
|
||||||
@@ -108,6 +112,8 @@ export interface envVarTypes {
|
|||||||
LINKEDIN_CLIENT_SECRET: string;
|
LINKEDIN_CLIENT_SECRET: string;
|
||||||
APPLE_CLIENT_ID: string;
|
APPLE_CLIENT_ID: string;
|
||||||
APPLE_CLIENT_SECRET: string;
|
APPLE_CLIENT_SECRET: string;
|
||||||
|
TWITTER_CLIENT_ID: string;
|
||||||
|
TWITTER_CLIENT_SECRET: string;
|
||||||
ROLES: [string] | [];
|
ROLES: [string] | [];
|
||||||
DEFAULT_ROLES: [string] | [];
|
DEFAULT_ROLES: [string] | [];
|
||||||
PROTECTED_ROLES: [string] | [];
|
PROTECTED_ROLES: [string] | [];
|
||||||
@@ -138,6 +144,8 @@ export interface envVarTypes {
|
|||||||
DATABASE_TYPE: string;
|
DATABASE_TYPE: string;
|
||||||
DATABASE_URL: string;
|
DATABASE_URL: string;
|
||||||
ACCESS_TOKEN_EXPIRY_TIME: string;
|
ACCESS_TOKEN_EXPIRY_TIME: string;
|
||||||
|
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
||||||
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const envSubViews = {
|
export const envSubViews = {
|
||||||
@@ -168,6 +176,7 @@ export enum EmailTemplateInputDataFields {
|
|||||||
SUBJECT = 'subject',
|
SUBJECT = 'subject',
|
||||||
CREATED_AT = 'created_at',
|
CREATED_AT = 'created_at',
|
||||||
TEMPLATE = 'template',
|
TEMPLATE = 'template',
|
||||||
|
DESIGN = 'design',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum WebhookInputHeaderFields {
|
export enum WebhookInputHeaderFields {
|
||||||
@@ -207,27 +216,90 @@ export enum webhookVerifiedStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const emailTemplateVariables = {
|
export const emailTemplateVariables = {
|
||||||
'user.id': '{.user.id}}',
|
'user.id': {
|
||||||
'user.email': '{.user.email}}',
|
description: `User identifier`,
|
||||||
'user.given_name': '{.user.given_name}}',
|
value: '{.user.id}}',
|
||||||
'user.family_name': '{.user.family_name}}',
|
},
|
||||||
'user.signup_methods': '{.user.signup_methods}}',
|
'user.email': {
|
||||||
'user.email_verified': '{.user.email_verified}}',
|
description: 'User email address',
|
||||||
'user.picture': '{.user.picture}}',
|
value: '{.user.email}}',
|
||||||
'user.roles': '{.user.roles}}',
|
},
|
||||||
'user.middle_name': '{.user.middle_name}}',
|
'user.given_name': {
|
||||||
'user.nickname': '{.user.nickname}}',
|
description: `User first name`,
|
||||||
'user.preferred_username': '{.user.preferred_username}}',
|
value: '{.user.given_name}}',
|
||||||
'user.gender': '{.user.gender}}',
|
},
|
||||||
'user.birthdate': '{.user.birthdate}}',
|
'user.family_name': {
|
||||||
'user.phone_number': '{.user.phone_number}}',
|
description: `User last name`,
|
||||||
'user.phone_number_verified': '{.user.phone_number_verified}}',
|
value: '{.user.family_name}}',
|
||||||
'user.created_at': '{.user.created_at}}',
|
},
|
||||||
'user.updated_at': '{.user.updated_at}}',
|
'user.middle_name': {
|
||||||
'organization.name': '{.organization.name}}',
|
description: `Middle name of user`,
|
||||||
'organization.logo': '{.organization.logo}}',
|
value: '{.user.middle_name}}',
|
||||||
verification_url: '{.verification_url}}',
|
},
|
||||||
otp: '{.otp}}',
|
'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 = `{
|
export const webhookPayloadExample: string = `{
|
||||||
|
@@ -18,48 +18,52 @@ export const AdminSessionQuery = `
|
|||||||
export const EnvVariablesQuery = `
|
export const EnvVariablesQuery = `
|
||||||
query {
|
query {
|
||||||
_env{
|
_env{
|
||||||
CLIENT_ID,
|
CLIENT_ID
|
||||||
CLIENT_SECRET,
|
CLIENT_SECRET
|
||||||
GOOGLE_CLIENT_ID,
|
GOOGLE_CLIENT_ID
|
||||||
GOOGLE_CLIENT_SECRET,
|
GOOGLE_CLIENT_SECRET
|
||||||
GITHUB_CLIENT_ID,
|
GITHUB_CLIENT_ID
|
||||||
GITHUB_CLIENT_SECRET,
|
GITHUB_CLIENT_SECRET
|
||||||
FACEBOOK_CLIENT_ID,
|
FACEBOOK_CLIENT_ID
|
||||||
FACEBOOK_CLIENT_SECRET,
|
FACEBOOK_CLIENT_SECRET
|
||||||
LINKEDIN_CLIENT_ID,
|
LINKEDIN_CLIENT_ID
|
||||||
LINKEDIN_CLIENT_SECRET,
|
LINKEDIN_CLIENT_SECRET
|
||||||
APPLE_CLIENT_ID,
|
APPLE_CLIENT_ID
|
||||||
APPLE_CLIENT_SECRET,
|
APPLE_CLIENT_SECRET
|
||||||
DEFAULT_ROLES,
|
TWITTER_CLIENT_ID
|
||||||
PROTECTED_ROLES,
|
TWITTER_CLIENT_SECRET
|
||||||
ROLES,
|
DEFAULT_ROLES
|
||||||
JWT_TYPE,
|
PROTECTED_ROLES
|
||||||
JWT_SECRET,
|
ROLES
|
||||||
JWT_ROLE_CLAIM,
|
JWT_TYPE
|
||||||
JWT_PRIVATE_KEY,
|
JWT_SECRET
|
||||||
JWT_PUBLIC_KEY,
|
JWT_ROLE_CLAIM
|
||||||
REDIS_URL,
|
JWT_PRIVATE_KEY
|
||||||
SMTP_HOST,
|
JWT_PUBLIC_KEY
|
||||||
SMTP_PORT,
|
REDIS_URL
|
||||||
SMTP_USERNAME,
|
SMTP_HOST
|
||||||
SMTP_PASSWORD,
|
SMTP_PORT
|
||||||
SENDER_EMAIL,
|
SMTP_USERNAME
|
||||||
ALLOWED_ORIGINS,
|
SMTP_PASSWORD
|
||||||
ORGANIZATION_NAME,
|
SENDER_EMAIL
|
||||||
ORGANIZATION_LOGO,
|
ALLOWED_ORIGINS
|
||||||
ADMIN_SECRET,
|
ORGANIZATION_NAME
|
||||||
DISABLE_LOGIN_PAGE,
|
ORGANIZATION_LOGO
|
||||||
DISABLE_MAGIC_LINK_LOGIN,
|
ADMIN_SECRET
|
||||||
DISABLE_EMAIL_VERIFICATION,
|
DISABLE_LOGIN_PAGE
|
||||||
DISABLE_BASIC_AUTHENTICATION,
|
DISABLE_MAGIC_LINK_LOGIN
|
||||||
DISABLE_SIGN_UP,
|
DISABLE_EMAIL_VERIFICATION
|
||||||
DISABLE_STRONG_PASSWORD,
|
DISABLE_BASIC_AUTHENTICATION
|
||||||
DISABLE_REDIS_FOR_ENV,
|
DISABLE_SIGN_UP
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT,
|
DISABLE_STRONG_PASSWORD
|
||||||
DATABASE_NAME,
|
DISABLE_REDIS_FOR_ENV
|
||||||
DATABASE_TYPE,
|
CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||||
DATABASE_URL,
|
DATABASE_NAME
|
||||||
ACCESS_TOKEN_EXPIRY_TIME,
|
DATABASE_TYPE
|
||||||
|
DATABASE_URL
|
||||||
|
ACCESS_TOKEN_EXPIRY_TIME
|
||||||
|
DISABLE_MULTI_FACTOR_AUTHENTICATION
|
||||||
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -132,6 +136,7 @@ export const EmailTemplatesQuery = `
|
|||||||
subject
|
subject
|
||||||
created_at
|
created_at
|
||||||
template
|
template
|
||||||
|
design
|
||||||
}
|
}
|
||||||
pagination {
|
pagination {
|
||||||
limit
|
limit
|
||||||
|
@@ -58,6 +58,7 @@ interface EmailTemplateDataType {
|
|||||||
[EmailTemplateInputDataFields.SUBJECT]: string;
|
[EmailTemplateInputDataFields.SUBJECT]: string;
|
||||||
[EmailTemplateInputDataFields.CREATED_AT]: number;
|
[EmailTemplateInputDataFields.CREATED_AT]: number;
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
||||||
|
[EmailTemplateInputDataFields.DESIGN]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmailTemplates = () => {
|
const EmailTemplates = () => {
|
||||||
|
@@ -50,6 +50,8 @@ const Environment = () => {
|
|||||||
LINKEDIN_CLIENT_SECRET: '',
|
LINKEDIN_CLIENT_SECRET: '',
|
||||||
APPLE_CLIENT_ID: '',
|
APPLE_CLIENT_ID: '',
|
||||||
APPLE_CLIENT_SECRET: '',
|
APPLE_CLIENT_SECRET: '',
|
||||||
|
TWITTER_CLIENT_ID: '',
|
||||||
|
TWITTER_CLIENT_SECRET: '',
|
||||||
ROLES: [],
|
ROLES: [],
|
||||||
DEFAULT_ROLES: [],
|
DEFAULT_ROLES: [],
|
||||||
PROTECTED_ROLES: [],
|
PROTECTED_ROLES: [],
|
||||||
@@ -80,6 +82,8 @@ const Environment = () => {
|
|||||||
DATABASE_TYPE: '',
|
DATABASE_TYPE: '',
|
||||||
DATABASE_URL: '',
|
DATABASE_URL: '',
|
||||||
ACCESS_TOKEN_EXPIRY_TIME: '',
|
ACCESS_TOKEN_EXPIRY_TIME: '',
|
||||||
|
DISABLE_MULTI_FACTOR_AUTHENTICATION: false,
|
||||||
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [fieldVisibility, setFieldVisibility] = React.useState<
|
const [fieldVisibility, setFieldVisibility] = React.useState<
|
||||||
@@ -90,6 +94,7 @@ const Environment = () => {
|
|||||||
FACEBOOK_CLIENT_SECRET: false,
|
FACEBOOK_CLIENT_SECRET: false,
|
||||||
LINKEDIN_CLIENT_SECRET: false,
|
LINKEDIN_CLIENT_SECRET: false,
|
||||||
APPLE_CLIENT_SECRET: false,
|
APPLE_CLIENT_SECRET: false,
|
||||||
|
TWITTER_CLIENT_SECRET: false,
|
||||||
JWT_SECRET: false,
|
JWT_SECRET: false,
|
||||||
SMTP_PASSWORD: false,
|
SMTP_PASSWORD: false,
|
||||||
ADMIN_SECRET: false,
|
ADMIN_SECRET: false,
|
||||||
|
@@ -29,6 +29,7 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
useToast,
|
useToast,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
TableContainer
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FaAngleLeft,
|
FaAngleLeft,
|
||||||
@@ -262,9 +263,8 @@ export default function Users() {
|
|||||||
.toPromise();
|
.toPromise();
|
||||||
if (res.data?._update_user?.id) {
|
if (res.data?._update_user?.id) {
|
||||||
toast({
|
toast({
|
||||||
title: `Multi factor authentication ${
|
title: `Multi factor authentication ${user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
|
||||||
user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
|
} for user`,
|
||||||
} for user`,
|
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
status: 'success',
|
status: 'success',
|
||||||
position: 'bottom-right',
|
position: 'bottom-right',
|
||||||
@@ -293,256 +293,262 @@ export default function Users() {
|
|||||||
</Flex>
|
</Flex>
|
||||||
{!loading ? (
|
{!loading ? (
|
||||||
userList.length > 0 ? (
|
userList.length > 0 ? (
|
||||||
<Table variant="simple">
|
<TableContainer>
|
||||||
<Thead>
|
<Table variant="simple">
|
||||||
<Tr>
|
<Thead>
|
||||||
<Th>Email</Th>
|
<Tr>
|
||||||
<Th>Created At</Th>
|
<Th>Email</Th>
|
||||||
<Th>Signup Methods</Th>
|
<Th>Created At</Th>
|
||||||
<Th>Roles</Th>
|
<Th>Signup Methods</Th>
|
||||||
<Th>Verified</Th>
|
<Th>Roles</Th>
|
||||||
<Th>Access</Th>
|
<Th>Verified</Th>
|
||||||
<Th>MFA</Th>
|
<Th>Access</Th>
|
||||||
<Th>Actions</Th>
|
<Th>
|
||||||
</Tr>
|
<Tooltip label="MultiFactor Authentication Enabled / Disabled">
|
||||||
</Thead>
|
MFA
|
||||||
<Tbody>
|
</Tooltip>
|
||||||
{userList.map((user: userDataTypes) => {
|
</Th>
|
||||||
const { email_verified, created_at, ...rest }: any = user;
|
<Th>Actions</Th>
|
||||||
return (
|
</Tr>
|
||||||
<Tr key={user.id} style={{ fontSize: 14 }}>
|
</Thead>
|
||||||
<Td maxW="300">{user.email}</Td>
|
<Tbody>
|
||||||
<Td>
|
{userList.map((user: userDataTypes) => {
|
||||||
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
|
const { email_verified, created_at, ...rest }: any = user;
|
||||||
</Td>
|
return (
|
||||||
<Td>{user.signup_methods}</Td>
|
<Tr key={user.id} style={{ fontSize: 14 }}>
|
||||||
<Td>{user.roles.join(', ')}</Td>
|
<Td maxW="300">{user.email}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<Tag
|
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
|
||||||
size="sm"
|
</Td>
|
||||||
variant="outline"
|
<Td>{user.signup_methods}</Td>
|
||||||
colorScheme={user.email_verified ? 'green' : 'yellow'}
|
<Td>{user.roles.join(', ')}</Td>
|
||||||
>
|
<Td>
|
||||||
{user.email_verified.toString()}
|
<Tag
|
||||||
</Tag>
|
size="sm"
|
||||||
</Td>
|
variant="outline"
|
||||||
<Td>
|
colorScheme={user.email_verified ? 'green' : 'yellow'}
|
||||||
<Tag
|
>
|
||||||
size="sm"
|
{user.email_verified.toString()}
|
||||||
variant="outline"
|
</Tag>
|
||||||
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
|
</Td>
|
||||||
>
|
<Td>
|
||||||
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
|
<Tag
|
||||||
</Tag>
|
size="sm"
|
||||||
</Td>
|
variant="outline"
|
||||||
<Td>
|
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
|
||||||
<Tag
|
>
|
||||||
size="sm"
|
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
|
||||||
variant="outline"
|
</Tag>
|
||||||
colorScheme={
|
</Td>
|
||||||
user.is_multi_factor_auth_enabled ? 'green' : 'red'
|
<Td>
|
||||||
}
|
<Tag
|
||||||
>
|
size="sm"
|
||||||
{user.is_multi_factor_auth_enabled
|
variant="outline"
|
||||||
? 'Enabled'
|
colorScheme={
|
||||||
: 'Disabled'}
|
user.is_multi_factor_auth_enabled ? 'green' : 'red'
|
||||||
</Tag>
|
}
|
||||||
</Td>
|
>
|
||||||
<Td>
|
{user.is_multi_factor_auth_enabled
|
||||||
<Menu>
|
? 'Enabled'
|
||||||
<MenuButton as={Button} variant="unstyled" size="sm">
|
: 'Disabled'}
|
||||||
<Flex
|
</Tag>
|
||||||
justifyContent="space-between"
|
</Td>
|
||||||
alignItems="center"
|
<Td>
|
||||||
>
|
<Menu>
|
||||||
<Text fontSize="sm" fontWeight="light">
|
<MenuButton as={Button} variant="unstyled" size="sm">
|
||||||
Menu
|
<Flex
|
||||||
</Text>
|
justifyContent="space-between"
|
||||||
<FaAngleDown style={{ marginLeft: 10 }} />
|
alignItems="center"
|
||||||
</Flex>
|
|
||||||
</MenuButton>
|
|
||||||
<MenuList>
|
|
||||||
{!user.email_verified && (
|
|
||||||
<MenuItem
|
|
||||||
onClick={() => userVerificationHandler(user)}
|
|
||||||
>
|
>
|
||||||
Verify User
|
<Text fontSize="sm" fontWeight="light">
|
||||||
</MenuItem>
|
Menu
|
||||||
)}
|
</Text>
|
||||||
<EditUserModal
|
<FaAngleDown style={{ marginLeft: 10 }} />
|
||||||
user={rest}
|
</Flex>
|
||||||
updateUserList={updateUserList}
|
</MenuButton>
|
||||||
/>
|
<MenuList>
|
||||||
<DeleteUserModal
|
{!user.email_verified && (
|
||||||
user={rest}
|
<MenuItem
|
||||||
updateUserList={updateUserList}
|
onClick={() => userVerificationHandler(user)}
|
||||||
/>
|
>
|
||||||
{user.revoked_timestamp ? (
|
Verify User
|
||||||
<MenuItem
|
</MenuItem>
|
||||||
onClick={() =>
|
)}
|
||||||
updateAccessHandler(
|
<EditUserModal
|
||||||
user.id,
|
user={rest}
|
||||||
updateAccessActions.ENABLE
|
updateUserList={updateUserList}
|
||||||
)
|
/>
|
||||||
}
|
<DeleteUserModal
|
||||||
>
|
user={rest}
|
||||||
Enable Access
|
updateUserList={updateUserList}
|
||||||
</MenuItem>
|
/>
|
||||||
) : (
|
{user.revoked_timestamp ? (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
updateAccessHandler(
|
updateAccessHandler(
|
||||||
user.id,
|
user.id,
|
||||||
updateAccessActions.REVOKE
|
updateAccessActions.ENABLE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Revoke Access
|
Enable Access
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
) : (
|
||||||
{user.is_multi_factor_auth_enabled ? (
|
<MenuItem
|
||||||
<MenuItem
|
onClick={() =>
|
||||||
onClick={() => multiFactorAuthUpdateHandler(user)}
|
updateAccessHandler(
|
||||||
>
|
user.id,
|
||||||
Disable MFA
|
updateAccessActions.REVOKE
|
||||||
</MenuItem>
|
)
|
||||||
) : (
|
}
|
||||||
<MenuItem
|
>
|
||||||
onClick={() => multiFactorAuthUpdateHandler(user)}
|
Revoke Access
|
||||||
>
|
</MenuItem>
|
||||||
Enable MFA
|
)}
|
||||||
</MenuItem>
|
{user.is_multi_factor_auth_enabled ? (
|
||||||
)}
|
<MenuItem
|
||||||
</MenuList>
|
onClick={() => multiFactorAuthUpdateHandler(user)}
|
||||||
</Menu>
|
>
|
||||||
</Td>
|
Disable MultiFactor Authentication
|
||||||
</Tr>
|
</MenuItem>
|
||||||
);
|
) : (
|
||||||
})}
|
<MenuItem
|
||||||
</Tbody>
|
onClick={() => multiFactorAuthUpdateHandler(user)}
|
||||||
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
|
>
|
||||||
<TableCaption>
|
Enable MultiFactor Authentication
|
||||||
<Flex
|
</MenuItem>
|
||||||
justifyContent="space-between"
|
)}
|
||||||
alignItems="center"
|
</MenuList>
|
||||||
m="2% 0"
|
</Menu>
|
||||||
>
|
</Td>
|
||||||
<Flex flex="1">
|
</Tr>
|
||||||
<Tooltip label="First Page">
|
);
|
||||||
<IconButton
|
})}
|
||||||
aria-label="icon button"
|
</Tbody>
|
||||||
onClick={() =>
|
{(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({
|
paginationHandler({
|
||||||
page: 1,
|
page: 1,
|
||||||
|
limit: parseInt(e.target.value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
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 />
|
{getLimits(paginationProps).map((pageSize) => (
|
||||||
<NumberInputStepper>
|
<option key={pageSize} value={pageSize}>
|
||||||
<NumberIncrementStepper />
|
Show {pageSize}
|
||||||
<NumberDecrementStepper />
|
</option>
|
||||||
</NumberInputStepper>
|
))}
|
||||||
</NumberInput>
|
</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>
|
||||||
<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 flex="1">
|
</TableCaption>
|
||||||
<Tooltip label="Next Page">
|
)}
|
||||||
<IconButton
|
</Table>
|
||||||
aria-label="icon button"
|
</TableContainer>
|
||||||
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
|
<Flex
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
|
@@ -29,19 +29,16 @@ const fallbackCopyTextToClipboard = (text: string) => {
|
|||||||
document.body.removeChild(textArea);
|
document.body.removeChild(textArea);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const copyTextToClipboard = (text: string) => {
|
export const copyTextToClipboard = async (text: string) => {
|
||||||
if (!navigator.clipboard) {
|
if (!navigator.clipboard) {
|
||||||
fallbackCopyTextToClipboard(text);
|
fallbackCopyTextToClipboard(text);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigator.clipboard.writeText(text).then(
|
try {
|
||||||
() => {
|
navigator.clipboard.writeText(text);
|
||||||
console.log('Async: Copying to clipboard was successful!');
|
} catch (err) {
|
||||||
},
|
throw err;
|
||||||
(err) => {
|
}
|
||||||
console.error('Async: Could not copy text: ', err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getObjectDiff = (obj1: any, obj2: any) => {
|
export const getObjectDiff = (obj1: any, obj2: any) => {
|
||||||
|
@@ -15,4 +15,6 @@ const (
|
|||||||
AuthRecipeMethodLinkedIn = "linkedin"
|
AuthRecipeMethodLinkedIn = "linkedin"
|
||||||
// AuthRecipeMethodApple is the apple auth method
|
// AuthRecipeMethodApple is the apple auth method
|
||||||
AuthRecipeMethodApple = "apple"
|
AuthRecipeMethodApple = "apple"
|
||||||
|
// AuthRecipeMethodTwitter is the twitter auth method
|
||||||
|
AuthRecipeMethodTwitter = "twitter"
|
||||||
)
|
)
|
||||||
|
@@ -49,6 +49,10 @@ const (
|
|||||||
EnvKeySenderEmail = "SENDER_EMAIL"
|
EnvKeySenderEmail = "SENDER_EMAIL"
|
||||||
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
|
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
|
||||||
EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED"
|
EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED"
|
||||||
|
// EnvKeyAppCookieSecure key for env variable APP_COOKIE_SECURE
|
||||||
|
EnvKeyAppCookieSecure = "APP_COOKIE_SECURE"
|
||||||
|
// EnvKeyAdminCookieSecure key for env variable ADMIN_COOKIE_SECURE
|
||||||
|
EnvKeyAdminCookieSecure = "ADMIN_COOKIE_SECURE"
|
||||||
// EnvKeyJwtType key for env variable JWT_TYPE
|
// EnvKeyJwtType key for env variable JWT_TYPE
|
||||||
EnvKeyJwtType = "JWT_TYPE"
|
EnvKeyJwtType = "JWT_TYPE"
|
||||||
// EnvKeyJwtSecret key for env variable JWT_SECRET
|
// EnvKeyJwtSecret key for env variable JWT_SECRET
|
||||||
@@ -85,6 +89,10 @@ const (
|
|||||||
EnvKeyAppleClientID = "APPLE_CLIENT_ID"
|
EnvKeyAppleClientID = "APPLE_CLIENT_ID"
|
||||||
// EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET
|
// EnvKeyAppleClientSecret key for env variable APPLE_CLIENT_SECRET
|
||||||
EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET"
|
EnvKeyAppleClientSecret = "APPLE_CLIENT_SECRET"
|
||||||
|
// EnvKeyTwitterClientID key for env variable TWITTER_CLIENT_ID
|
||||||
|
EnvKeyTwitterClientID = "TWITTER_CLIENT_ID"
|
||||||
|
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET
|
||||||
|
EnvKeyTwitterClientSecret = "TWITTER_CLIENT_SECRET"
|
||||||
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
|
// EnvKeyOrganizationName key for env variable ORGANIZATION_NAME
|
||||||
EnvKeyOrganizationName = "ORGANIZATION_NAME"
|
EnvKeyOrganizationName = "ORGANIZATION_NAME"
|
||||||
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
|
// EnvKeyOrganizationLogo key for env variable ORGANIZATION_LOGO
|
||||||
|
@@ -9,9 +9,11 @@ const (
|
|||||||
// Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token
|
// Ref: https://docs.github.com/en/developers/apps/building-github-apps/identifying-and-authorizing-users-for-github-apps#3-your-github-app-accesses-the-api-with-the-users-access-token
|
||||||
GithubUserInfoURL = "https://api.github.com/user"
|
GithubUserInfoURL = "https://api.github.com/user"
|
||||||
// Get github user emails when user info email is empty Ref: https://stackoverflow.com/a/35387123
|
// Get github user emails when user info email is empty Ref: https://stackoverflow.com/a/35387123
|
||||||
GithubUserEmails = "https://api/github.com/user/emails"
|
GithubUserEmails = "https://api.github.com/user/emails"
|
||||||
|
|
||||||
// Ref: https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api
|
// Ref: https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api
|
||||||
LinkedInUserInfoURL = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,emailAddress,profilePicture(displayImage~:playableStreams))"
|
LinkedInUserInfoURL = "https://api.linkedin.com/v2/me?projection=(id,localizedFirstName,localizedLastName,emailAddress,profilePicture(displayImage~:playableStreams))"
|
||||||
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"
|
LinkedInEmailURL = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"
|
||||||
|
|
||||||
|
TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username"
|
||||||
)
|
)
|
||||||
|
@@ -12,5 +12,5 @@ const (
|
|||||||
// VerificationTypeInviteMember is the invite_member verification type
|
// VerificationTypeInviteMember is the invite_member verification type
|
||||||
VerificationTypeInviteMember = "invite_member"
|
VerificationTypeInviteMember = "invite_member"
|
||||||
// VerificationTypeOTP is the otp verification type
|
// VerificationTypeOTP is the otp verification type
|
||||||
VerificationTypeOTP = "otp"
|
VerificationTypeOTP = "verify_otp"
|
||||||
)
|
)
|
||||||
|
@@ -3,15 +3,24 @@ package cookie
|
|||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
"github.com/authorizerdev/authorizer/server/parsers"
|
"github.com/authorizerdev/authorizer/server/parsers"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetAdminCookie sets the admin cookie in the response
|
// SetAdminCookie sets the admin cookie in the response
|
||||||
func SetAdminCookie(gc *gin.Context, token string) {
|
func SetAdminCookie(gc *gin.Context, token string) {
|
||||||
secure := true
|
adminCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAdminCookieSecure)
|
||||||
httpOnly := true
|
if err != nil {
|
||||||
|
log.Debug("Error while getting admin cookie secure from env variable: %v", err)
|
||||||
|
adminCookieSecure = true
|
||||||
|
}
|
||||||
|
|
||||||
|
secure := adminCookieSecure
|
||||||
|
httpOnly := adminCookieSecure
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
gc.SetCookie(constants.AdminCookieName, token, 3600, "/", host, secure, httpOnly)
|
gc.SetCookie(constants.AdminCookieName, token, 3600, "/", host, secure, httpOnly)
|
||||||
@@ -35,8 +44,14 @@ func GetAdminCookie(gc *gin.Context) (string, error) {
|
|||||||
|
|
||||||
// DeleteAdminCookie sets the response cookie to empty
|
// DeleteAdminCookie sets the response cookie to empty
|
||||||
func DeleteAdminCookie(gc *gin.Context) {
|
func DeleteAdminCookie(gc *gin.Context) {
|
||||||
secure := true
|
adminCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAdminCookieSecure)
|
||||||
httpOnly := true
|
if err != nil {
|
||||||
|
log.Debug("Error while getting admin cookie secure from env variable: %v", err)
|
||||||
|
adminCookieSecure = true
|
||||||
|
}
|
||||||
|
|
||||||
|
secure := adminCookieSecure
|
||||||
|
httpOnly := adminCookieSecure
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
gc.SetCookie(constants.AdminCookieName, "", -1, "/", host, secure, httpOnly)
|
gc.SetCookie(constants.AdminCookieName, "", -1, "/", host, secure, httpOnly)
|
||||||
|
@@ -4,15 +4,24 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
"github.com/authorizerdev/authorizer/server/parsers"
|
"github.com/authorizerdev/authorizer/server/parsers"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetSession sets the session cookie in the response
|
// SetSession sets the session cookie in the response
|
||||||
func SetSession(gc *gin.Context, sessionID string) {
|
func SetSession(gc *gin.Context, sessionID string) {
|
||||||
secure := true
|
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
|
||||||
httpOnly := true
|
if err != nil {
|
||||||
|
log.Debug("Error while getting app cookie secure from env variable: %v", err)
|
||||||
|
appCookieSecure = true
|
||||||
|
}
|
||||||
|
|
||||||
|
secure := appCookieSecure
|
||||||
|
httpOnly := appCookieSecure
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
domain := parsers.GetDomainName(hostname)
|
domain := parsers.GetDomainName(hostname)
|
||||||
@@ -30,8 +39,14 @@ func SetSession(gc *gin.Context, sessionID string) {
|
|||||||
|
|
||||||
// DeleteSession sets session cookies to expire
|
// DeleteSession sets session cookies to expire
|
||||||
func DeleteSession(gc *gin.Context) {
|
func DeleteSession(gc *gin.Context) {
|
||||||
secure := true
|
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
|
||||||
httpOnly := true
|
if err != nil {
|
||||||
|
log.Debug("Error while getting app cookie secure from env variable: %v", err)
|
||||||
|
appCookieSecure = true
|
||||||
|
}
|
||||||
|
|
||||||
|
secure := appCookieSecure
|
||||||
|
httpOnly := appCookieSecure
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
host, _ := parsers.GetHostParts(hostname)
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
domain := parsers.GetDomainName(hostname)
|
domain := parsers.GetDomainName(hostname)
|
||||||
|
@@ -14,6 +14,7 @@ type EmailTemplate struct {
|
|||||||
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
|
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
|
||||||
Subject string `gorm:"type:text" json:"subject" bson:"subject" cql:"subject"`
|
Subject string `gorm:"type:text" json:"subject" bson:"subject" cql:"subject"`
|
||||||
Template string `gorm:"type:text" json:"template" bson:"template" cql:"template"`
|
Template string `gorm:"type:text" json:"template" bson:"template" cql:"template"`
|
||||||
|
Design string `gorm:"type:text" json:"design" bson:"design" cql:"design"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
|
||||||
}
|
}
|
||||||
@@ -29,6 +30,7 @@ func (e *EmailTemplate) AsAPIEmailTemplate() *model.EmailTemplate {
|
|||||||
EventName: e.EventName,
|
EventName: e.EventName,
|
||||||
Subject: e.Subject,
|
Subject: e.Subject,
|
||||||
Template: e.Template,
|
Template: e.Template,
|
||||||
|
Design: e.Design,
|
||||||
CreatedAt: refs.NewInt64Ref(e.CreatedAt),
|
CreatedAt: refs.NewInt64Ref(e.CreatedAt),
|
||||||
UpdatedAt: refs.NewInt64Ref(e.UpdatedAt),
|
UpdatedAt: refs.NewInt64Ref(e.UpdatedAt),
|
||||||
}
|
}
|
||||||
|
@@ -29,7 +29,7 @@ func (p *provider) AddEmailTemplate(ctx context.Context, emailTemplate models.Em
|
|||||||
return nil, fmt.Errorf("Email template with %s event_name already exists", emailTemplate.EventName)
|
return nil, fmt.Errorf("Email template with %s event_name already exists", emailTemplate.EventName)
|
||||||
}
|
}
|
||||||
|
|
||||||
insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, subject, template, created_at, updated_at) VALUES ('%s', '%s', '%s','%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Subject, emailTemplate.Template, emailTemplate.CreatedAt, emailTemplate.UpdatedAt)
|
insertQuery := fmt.Sprintf("INSERT INTO %s (id, event_name, subject, design, template, created_at, updated_at) VALUES ('%s', '%s', '%s','%s','%s', %d, %d)", KeySpace+"."+models.Collections.EmailTemplate, emailTemplate.ID, emailTemplate.EventName, emailTemplate.Subject, emailTemplate.Design, emailTemplate.Template, emailTemplate.CreatedAt, emailTemplate.UpdatedAt)
|
||||||
err := p.db.Query(insertQuery).Exec()
|
err := p.db.Query(insertQuery).Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -103,14 +103,14 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagin
|
|||||||
// there is no offset in cassandra
|
// there is no offset in cassandra
|
||||||
// so we fetch till limit + offset
|
// so we fetch till limit + offset
|
||||||
// and return the results from offset to limit
|
// and return the results from offset to limit
|
||||||
query := fmt.Sprintf("SELECT id, event_name, subject, template, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.EmailTemplate, pagination.Limit+pagination.Offset)
|
query := fmt.Sprintf("SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.EmailTemplate, pagination.Limit+pagination.Offset)
|
||||||
|
|
||||||
scanner := p.db.Query(query).Iter().Scanner()
|
scanner := p.db.Query(query).Iter().Scanner()
|
||||||
counter := int64(0)
|
counter := int64(0)
|
||||||
for scanner.Next() {
|
for scanner.Next() {
|
||||||
if counter >= pagination.Offset {
|
if counter >= pagination.Offset {
|
||||||
var emailTemplate models.EmailTemplate
|
var emailTemplate models.EmailTemplate
|
||||||
err := scanner.Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
|
err := scanner.Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -128,8 +128,8 @@ func (p *provider) ListEmailTemplate(ctx context.Context, pagination model.Pagin
|
|||||||
// GetEmailTemplateByID to get EmailTemplate by id
|
// GetEmailTemplateByID to get EmailTemplate by id
|
||||||
func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) {
|
func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error) {
|
||||||
var emailTemplate models.EmailTemplate
|
var emailTemplate models.EmailTemplate
|
||||||
query := fmt.Sprintf(`SELECT id, event_name, subject, template, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.EmailTemplate, emailTemplateID)
|
query := fmt.Sprintf(`SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1`, KeySpace+"."+models.Collections.EmailTemplate, emailTemplateID)
|
||||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
|
err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -139,8 +139,8 @@ func (p *provider) GetEmailTemplateByID(ctx context.Context, emailTemplateID str
|
|||||||
// GetEmailTemplateByEventName to get EmailTemplate by event_name
|
// GetEmailTemplateByEventName to get EmailTemplate by event_name
|
||||||
func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) {
|
func (p *provider) GetEmailTemplateByEventName(ctx context.Context, eventName string) (*model.EmailTemplate, error) {
|
||||||
var emailTemplate models.EmailTemplate
|
var emailTemplate models.EmailTemplate
|
||||||
query := fmt.Sprintf(`SELECT id, event_name, subject, template, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.EmailTemplate, eventName)
|
query := fmt.Sprintf(`SELECT id, event_name, subject, design, template, created_at, updated_at FROM %s WHERE event_name = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.EmailTemplate, eventName)
|
||||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
|
err := p.db.Query(query).Consistency(gocql.One).Scan(&emailTemplate.ID, &emailTemplate.EventName, &emailTemplate.Subject, &emailTemplate.Design, &emailTemplate.Template, &emailTemplate.CreatedAt, &emailTemplate.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -224,10 +224,11 @@ func NewProvider() (*provider, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// add subject on email_templates table
|
// add subject on email_templates table
|
||||||
emailTemplateAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD subject text;`, KeySpace, models.Collections.EmailTemplate)
|
emailTemplateAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (subject text, design text);`, KeySpace, models.Collections.EmailTemplate)
|
||||||
err = session.Query(emailTemplateAlterQuery).Exec()
|
err = session.Query(emailTemplateAlterQuery).Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
log.Debug("Failed to alter table as column exists: ", err)
|
||||||
|
// continue
|
||||||
}
|
}
|
||||||
|
|
||||||
otpCollection := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, email text, otp text, expires_at bigint, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.OTP)
|
otpCollection := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, email text, otp text, expires_at bigint, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.OTP)
|
||||||
|
@@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
func getDefaultTemplate(event string) *model.EmailTemplate {
|
func getDefaultTemplate(event string) *model.EmailTemplate {
|
||||||
switch event {
|
switch event {
|
||||||
case constants.VerificationTypeBasicAuthSignup, constants.VerificationTypeMagicLinkLogin:
|
case constants.VerificationTypeBasicAuthSignup, constants.VerificationTypeMagicLinkLogin, constants.VerificationTypeUpdateEmail:
|
||||||
return &model.EmailTemplate{
|
return &model.EmailTemplate{
|
||||||
Subject: emailVerificationSubject,
|
Subject: emailVerificationSubject,
|
||||||
Template: emailVerificationTemplate,
|
Template: emailVerificationTemplate,
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
package email
|
|
60
server/env/env.go
vendored
60
server/env/env.go
vendored
@@ -72,11 +72,15 @@ func InitAllEnv() error {
|
|||||||
osLinkedInClientSecret := os.Getenv(constants.EnvKeyLinkedInClientSecret)
|
osLinkedInClientSecret := os.Getenv(constants.EnvKeyLinkedInClientSecret)
|
||||||
osAppleClientID := os.Getenv(constants.EnvKeyAppleClientID)
|
osAppleClientID := os.Getenv(constants.EnvKeyAppleClientID)
|
||||||
osAppleClientSecret := os.Getenv(constants.EnvKeyAppleClientSecret)
|
osAppleClientSecret := os.Getenv(constants.EnvKeyAppleClientSecret)
|
||||||
|
osTwitterClientID := os.Getenv(constants.EnvKeyTwitterClientID)
|
||||||
|
osTwitterClientSecret := os.Getenv(constants.EnvKeyTwitterClientSecret)
|
||||||
osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL)
|
osResetPasswordURL := os.Getenv(constants.EnvKeyResetPasswordURL)
|
||||||
osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName)
|
osOrganizationName := os.Getenv(constants.EnvKeyOrganizationName)
|
||||||
osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo)
|
osOrganizationLogo := os.Getenv(constants.EnvKeyOrganizationLogo)
|
||||||
|
|
||||||
// os bool vars
|
// os bool vars
|
||||||
|
osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure)
|
||||||
|
osAdminCookieSecure := os.Getenv(constants.EnvKeyAdminCookieSecure)
|
||||||
osDisableBasicAuthentication := os.Getenv(constants.EnvKeyDisableBasicAuthentication)
|
osDisableBasicAuthentication := os.Getenv(constants.EnvKeyDisableBasicAuthentication)
|
||||||
osDisableEmailVerification := os.Getenv(constants.EnvKeyDisableEmailVerification)
|
osDisableEmailVerification := os.Getenv(constants.EnvKeyDisableEmailVerification)
|
||||||
osDisableMagicLinkLogin := os.Getenv(constants.EnvKeyDisableMagicLinkLogin)
|
osDisableMagicLinkLogin := os.Getenv(constants.EnvKeyDisableMagicLinkLogin)
|
||||||
@@ -355,31 +359,45 @@ func InitAllEnv() error {
|
|||||||
if val, ok := envData[constants.EnvKeyLinkedInClientID]; !ok || val == "" {
|
if val, ok := envData[constants.EnvKeyLinkedInClientID]; !ok || val == "" {
|
||||||
envData[constants.EnvKeyLinkedInClientID] = osLinkedInClientID
|
envData[constants.EnvKeyLinkedInClientID] = osLinkedInClientID
|
||||||
}
|
}
|
||||||
if osFacebookClientID != "" && envData[constants.EnvKeyLinkedInClientID] != osFacebookClientID {
|
if osLinkedInClientID != "" && envData[constants.EnvKeyLinkedInClientID] != osLinkedInClientID {
|
||||||
envData[constants.EnvKeyLinkedInClientID] = osLinkedInClientID
|
envData[constants.EnvKeyLinkedInClientID] = osLinkedInClientID
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvKeyLinkedInClientSecret]; !ok || val == "" {
|
if val, ok := envData[constants.EnvKeyLinkedInClientSecret]; !ok || val == "" {
|
||||||
envData[constants.EnvKeyLinkedInClientSecret] = osLinkedInClientSecret
|
envData[constants.EnvKeyLinkedInClientSecret] = osLinkedInClientSecret
|
||||||
}
|
}
|
||||||
if osFacebookClientSecret != "" && envData[constants.EnvKeyLinkedInClientSecret] != osFacebookClientSecret {
|
if osLinkedInClientSecret != "" && envData[constants.EnvKeyLinkedInClientSecret] != osLinkedInClientSecret {
|
||||||
envData[constants.EnvKeyLinkedInClientSecret] = osLinkedInClientSecret
|
envData[constants.EnvKeyLinkedInClientSecret] = osLinkedInClientSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvKeyAppleClientID]; !ok || val == "" {
|
if val, ok := envData[constants.EnvKeyAppleClientID]; !ok || val == "" {
|
||||||
envData[constants.EnvKeyAppleClientID] = osAppleClientID
|
envData[constants.EnvKeyAppleClientID] = osAppleClientID
|
||||||
}
|
}
|
||||||
if osFacebookClientID != "" && envData[constants.EnvKeyAppleClientID] != osFacebookClientID {
|
if osAppleClientID != "" && envData[constants.EnvKeyAppleClientID] != osAppleClientID {
|
||||||
envData[constants.EnvKeyAppleClientID] = osAppleClientID
|
envData[constants.EnvKeyAppleClientID] = osAppleClientID
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvKeyAppleClientSecret]; !ok || val == "" {
|
if val, ok := envData[constants.EnvKeyAppleClientSecret]; !ok || val == "" {
|
||||||
envData[constants.EnvKeyAppleClientSecret] = osAppleClientSecret
|
envData[constants.EnvKeyAppleClientSecret] = osAppleClientSecret
|
||||||
}
|
}
|
||||||
if osFacebookClientSecret != "" && envData[constants.EnvKeyAppleClientSecret] != osFacebookClientSecret {
|
if osAppleClientSecret != "" && envData[constants.EnvKeyAppleClientSecret] != osAppleClientSecret {
|
||||||
envData[constants.EnvKeyAppleClientSecret] = osAppleClientSecret
|
envData[constants.EnvKeyAppleClientSecret] = osAppleClientSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := envData[constants.EnvKeyTwitterClientID]; !ok || val == "" {
|
||||||
|
envData[constants.EnvKeyTwitterClientID] = osTwitterClientID
|
||||||
|
}
|
||||||
|
if osTwitterClientID != "" && envData[constants.EnvKeyTwitterClientID] != osTwitterClientID {
|
||||||
|
envData[constants.EnvKeyTwitterClientID] = osTwitterClientID
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := envData[constants.EnvKeyTwitterClientSecret]; !ok || val == "" {
|
||||||
|
envData[constants.EnvKeyTwitterClientSecret] = osTwitterClientSecret
|
||||||
|
}
|
||||||
|
if osTwitterClientSecret != "" && envData[constants.EnvKeyTwitterClientSecret] != osTwitterClientSecret {
|
||||||
|
envData[constants.EnvKeyTwitterClientSecret] = osTwitterClientSecret
|
||||||
|
}
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvKeyResetPasswordURL]; !ok || val == "" {
|
if val, ok := envData[constants.EnvKeyResetPasswordURL]; !ok || val == "" {
|
||||||
envData[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(osResetPasswordURL, "/")
|
envData[constants.EnvKeyResetPasswordURL] = strings.TrimPrefix(osResetPasswordURL, "/")
|
||||||
}
|
}
|
||||||
@@ -401,6 +419,40 @@ func InitAllEnv() error {
|
|||||||
envData[constants.EnvKeyOrganizationLogo] = osOrganizationLogo
|
envData[constants.EnvKeyOrganizationLogo] = osOrganizationLogo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := envData[constants.EnvKeyAppCookieSecure]; !ok {
|
||||||
|
if osAppCookieSecure == "" {
|
||||||
|
envData[constants.EnvKeyAppCookieSecure] = true
|
||||||
|
} else {
|
||||||
|
envData[constants.EnvKeyAppCookieSecure] = osAppCookieSecure == "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if osAppCookieSecure != "" {
|
||||||
|
boolValue, err := strconv.ParseBool(osAppCookieSecure)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if boolValue != envData[constants.EnvKeyAppCookieSecure].(bool) {
|
||||||
|
envData[constants.EnvKeyAppCookieSecure] = boolValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := envData[constants.EnvKeyAdminCookieSecure]; !ok {
|
||||||
|
if osAdminCookieSecure == "" {
|
||||||
|
envData[constants.EnvKeyAdminCookieSecure] = true
|
||||||
|
} else {
|
||||||
|
envData[constants.EnvKeyAdminCookieSecure] = osAdminCookieSecure == "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if osAdminCookieSecure != "" {
|
||||||
|
boolValue, err := strconv.ParseBool(osAdminCookieSecure)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if boolValue != envData[constants.EnvKeyAdminCookieSecure].(bool) {
|
||||||
|
envData[constants.EnvKeyAdminCookieSecure] = boolValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if _, ok := envData[constants.EnvKeyDisableBasicAuthentication]; !ok {
|
if _, ok := envData[constants.EnvKeyDisableBasicAuthentication]; !ok {
|
||||||
envData[constants.EnvKeyDisableBasicAuthentication] = osDisableBasicAuthentication == "true"
|
envData[constants.EnvKeyDisableBasicAuthentication] = osDisableBasicAuthentication == "true"
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ type AddEmailTemplateRequest struct {
|
|||||||
EventName string `json:"event_name"`
|
EventName string `json:"event_name"`
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Template string `json:"template"`
|
Template string `json:"template"`
|
||||||
|
Design string `json:"design"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddWebhookRequest struct {
|
type AddWebhookRequest struct {
|
||||||
@@ -45,6 +46,7 @@ type EmailTemplate struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
EventName string `json:"event_name"`
|
EventName string `json:"event_name"`
|
||||||
Template string `json:"template"`
|
Template string `json:"template"`
|
||||||
|
Design string `json:"design"`
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at"`
|
||||||
@@ -104,6 +106,8 @@ type Env struct {
|
|||||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
||||||
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
||||||
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
||||||
|
TwitterClientID *string `json:"TWITTER_CLIENT_ID"`
|
||||||
|
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET"`
|
||||||
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
||||||
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
||||||
}
|
}
|
||||||
@@ -162,6 +166,7 @@ type Meta struct {
|
|||||||
IsGithubLoginEnabled bool `json:"is_github_login_enabled"`
|
IsGithubLoginEnabled bool `json:"is_github_login_enabled"`
|
||||||
IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"`
|
IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"`
|
||||||
IsAppleLoginEnabled bool `json:"is_apple_login_enabled"`
|
IsAppleLoginEnabled bool `json:"is_apple_login_enabled"`
|
||||||
|
IsTwitterLoginEnabled bool `json:"is_twitter_login_enabled"`
|
||||||
IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"`
|
IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"`
|
||||||
IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"`
|
IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"`
|
||||||
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
||||||
@@ -179,15 +184,17 @@ type PaginatedInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Pagination struct {
|
type Pagination struct {
|
||||||
Limit int64 `json:"limit"`
|
Limit int64 `json:"limit"`
|
||||||
Page int64 `json:"page"`
|
Page int64 `json:"page"`
|
||||||
Offset int64 `json:"offset"`
|
Cursor *string `json:"cursor"`
|
||||||
Total int64 `json:"total"`
|
Offset int64 `json:"offset"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PaginationInput struct {
|
type PaginationInput struct {
|
||||||
Limit *int64 `json:"limit"`
|
Limit *int64 `json:"limit"`
|
||||||
Page *int64 `json:"page"`
|
Page *int64 `json:"page"`
|
||||||
|
Cursor *string `json:"cursor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResendOTPRequest struct {
|
type ResendOTPRequest struct {
|
||||||
@@ -252,6 +259,7 @@ type UpdateEmailTemplateRequest struct {
|
|||||||
EventName *string `json:"event_name"`
|
EventName *string `json:"event_name"`
|
||||||
Template *string `json:"template"`
|
Template *string `json:"template"`
|
||||||
Subject *string `json:"subject"`
|
Subject *string `json:"subject"`
|
||||||
|
Design *string `json:"design"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateEnvInput struct {
|
type UpdateEnvInput struct {
|
||||||
@@ -294,6 +302,8 @@ type UpdateEnvInput struct {
|
|||||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
||||||
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
||||||
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
||||||
|
TwitterClientID *string `json:"TWITTER_CLIENT_ID"`
|
||||||
|
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET"`
|
||||||
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
||||||
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
||||||
}
|
}
|
||||||
|
@@ -6,484 +6,494 @@ scalar Map
|
|||||||
scalar Any
|
scalar Any
|
||||||
|
|
||||||
type Pagination {
|
type Pagination {
|
||||||
limit: Int64!
|
limit: Int64!
|
||||||
page: Int64!
|
page: Int64!
|
||||||
offset: Int64!
|
cursor: String
|
||||||
total: Int64!
|
offset: Int64!
|
||||||
|
total: Int64!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta {
|
type Meta {
|
||||||
version: String!
|
version: String!
|
||||||
client_id: String!
|
client_id: String!
|
||||||
is_google_login_enabled: Boolean!
|
is_google_login_enabled: Boolean!
|
||||||
is_facebook_login_enabled: Boolean!
|
is_facebook_login_enabled: Boolean!
|
||||||
is_github_login_enabled: Boolean!
|
is_github_login_enabled: Boolean!
|
||||||
is_linkedin_login_enabled: Boolean!
|
is_linkedin_login_enabled: Boolean!
|
||||||
is_apple_login_enabled: Boolean!
|
is_apple_login_enabled: Boolean!
|
||||||
is_email_verification_enabled: Boolean!
|
is_twitter_login_enabled: Boolean!
|
||||||
is_basic_authentication_enabled: Boolean!
|
is_email_verification_enabled: Boolean!
|
||||||
is_magic_link_login_enabled: Boolean!
|
is_basic_authentication_enabled: Boolean!
|
||||||
is_sign_up_enabled: Boolean!
|
is_magic_link_login_enabled: Boolean!
|
||||||
is_strong_password_enabled: Boolean!
|
is_sign_up_enabled: Boolean!
|
||||||
is_multi_factor_auth_enabled: Boolean!
|
is_strong_password_enabled: Boolean!
|
||||||
|
is_multi_factor_auth_enabled: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
id: ID!
|
id: ID!
|
||||||
email: String!
|
email: String!
|
||||||
email_verified: Boolean!
|
email_verified: Boolean!
|
||||||
signup_methods: String!
|
signup_methods: String!
|
||||||
given_name: String
|
given_name: String
|
||||||
family_name: String
|
family_name: String
|
||||||
middle_name: String
|
middle_name: String
|
||||||
nickname: String
|
nickname: String
|
||||||
# defaults to email
|
# defaults to email
|
||||||
preferred_username: String
|
preferred_username: String
|
||||||
gender: String
|
gender: String
|
||||||
birthdate: String
|
birthdate: String
|
||||||
phone_number: String
|
phone_number: String
|
||||||
phone_number_verified: Boolean
|
phone_number_verified: Boolean
|
||||||
picture: String
|
picture: String
|
||||||
roles: [String!]!
|
roles: [String!]!
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
revoked_timestamp: Int64
|
revoked_timestamp: Int64
|
||||||
is_multi_factor_auth_enabled: Boolean
|
is_multi_factor_auth_enabled: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type Users {
|
type Users {
|
||||||
pagination: Pagination!
|
pagination: Pagination!
|
||||||
users: [User!]!
|
users: [User!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequest {
|
type VerificationRequest {
|
||||||
id: ID!
|
id: ID!
|
||||||
identifier: String
|
identifier: String
|
||||||
token: String
|
token: String
|
||||||
email: String
|
email: String
|
||||||
expires: Int64
|
expires: Int64
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
nonce: String
|
nonce: String
|
||||||
redirect_uri: String
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequests {
|
type VerificationRequests {
|
||||||
pagination: Pagination!
|
pagination: Pagination!
|
||||||
verification_requests: [VerificationRequest!]!
|
verification_requests: [VerificationRequest!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Error {
|
type Error {
|
||||||
message: String!
|
message: String!
|
||||||
reason: String!
|
reason: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthResponse {
|
type AuthResponse {
|
||||||
message: String!
|
message: String!
|
||||||
should_show_otp_screen: Boolean
|
should_show_otp_screen: Boolean
|
||||||
access_token: String
|
access_token: String
|
||||||
id_token: String
|
id_token: String
|
||||||
refresh_token: String
|
refresh_token: String
|
||||||
expires_in: Int64
|
expires_in: Int64
|
||||||
user: User
|
user: User
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response {
|
type Response {
|
||||||
message: String!
|
message: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Env {
|
type Env {
|
||||||
ACCESS_TOKEN_EXPIRY_TIME: String
|
ACCESS_TOKEN_EXPIRY_TIME: String
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
DATABASE_NAME: String
|
DATABASE_NAME: String
|
||||||
DATABASE_URL: String
|
DATABASE_URL: String
|
||||||
DATABASE_TYPE: String
|
DATABASE_TYPE: String
|
||||||
DATABASE_USERNAME: String
|
DATABASE_USERNAME: String
|
||||||
DATABASE_PASSWORD: String
|
DATABASE_PASSWORD: String
|
||||||
DATABASE_HOST: String
|
DATABASE_HOST: String
|
||||||
DATABASE_PORT: String
|
DATABASE_PORT: String
|
||||||
CLIENT_ID: String!
|
CLIENT_ID: String!
|
||||||
CLIENT_SECRET: String!
|
CLIENT_SECRET: String!
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
||||||
SMTP_HOST: String
|
SMTP_HOST: String
|
||||||
SMTP_PORT: String
|
SMTP_PORT: String
|
||||||
SMTP_USERNAME: String
|
SMTP_USERNAME: String
|
||||||
SMTP_PASSWORD: String
|
SMTP_PASSWORD: String
|
||||||
SENDER_EMAIL: String
|
SENDER_EMAIL: String
|
||||||
JWT_TYPE: String
|
JWT_TYPE: String
|
||||||
JWT_SECRET: String
|
JWT_SECRET: String
|
||||||
JWT_PRIVATE_KEY: String
|
JWT_PRIVATE_KEY: String
|
||||||
JWT_PUBLIC_KEY: String
|
JWT_PUBLIC_KEY: String
|
||||||
ALLOWED_ORIGINS: [String!]
|
ALLOWED_ORIGINS: [String!]
|
||||||
APP_URL: String
|
APP_URL: String
|
||||||
REDIS_URL: String
|
REDIS_URL: String
|
||||||
RESET_PASSWORD_URL: String
|
RESET_PASSWORD_URL: String
|
||||||
DISABLE_EMAIL_VERIFICATION: Boolean!
|
DISABLE_EMAIL_VERIFICATION: Boolean!
|
||||||
DISABLE_BASIC_AUTHENTICATION: Boolean!
|
DISABLE_BASIC_AUTHENTICATION: Boolean!
|
||||||
DISABLE_MAGIC_LINK_LOGIN: Boolean!
|
DISABLE_MAGIC_LINK_LOGIN: Boolean!
|
||||||
DISABLE_LOGIN_PAGE: Boolean!
|
DISABLE_LOGIN_PAGE: Boolean!
|
||||||
DISABLE_SIGN_UP: Boolean!
|
DISABLE_SIGN_UP: Boolean!
|
||||||
DISABLE_REDIS_FOR_ENV: Boolean!
|
DISABLE_REDIS_FOR_ENV: Boolean!
|
||||||
DISABLE_STRONG_PASSWORD: Boolean!
|
DISABLE_STRONG_PASSWORD: Boolean!
|
||||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean!
|
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean!
|
||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean!
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean!
|
||||||
ROLES: [String!]
|
ROLES: [String!]
|
||||||
PROTECTED_ROLES: [String!]
|
PROTECTED_ROLES: [String!]
|
||||||
DEFAULT_ROLES: [String!]
|
DEFAULT_ROLES: [String!]
|
||||||
JWT_ROLE_CLAIM: String
|
JWT_ROLE_CLAIM: String
|
||||||
GOOGLE_CLIENT_ID: String
|
GOOGLE_CLIENT_ID: String
|
||||||
GOOGLE_CLIENT_SECRET: String
|
GOOGLE_CLIENT_SECRET: String
|
||||||
GITHUB_CLIENT_ID: String
|
GITHUB_CLIENT_ID: String
|
||||||
GITHUB_CLIENT_SECRET: String
|
GITHUB_CLIENT_SECRET: String
|
||||||
FACEBOOK_CLIENT_ID: String
|
FACEBOOK_CLIENT_ID: String
|
||||||
FACEBOOK_CLIENT_SECRET: String
|
FACEBOOK_CLIENT_SECRET: String
|
||||||
LINKEDIN_CLIENT_ID: String
|
LINKEDIN_CLIENT_ID: String
|
||||||
LINKEDIN_CLIENT_SECRET: String
|
LINKEDIN_CLIENT_SECRET: String
|
||||||
APPLE_CLIENT_ID: String
|
APPLE_CLIENT_ID: String
|
||||||
APPLE_CLIENT_SECRET: String
|
APPLE_CLIENT_SECRET: String
|
||||||
ORGANIZATION_NAME: String
|
TWITTER_CLIENT_ID: String
|
||||||
ORGANIZATION_LOGO: String
|
TWITTER_CLIENT_SECRET: String
|
||||||
|
ORGANIZATION_NAME: String
|
||||||
|
ORGANIZATION_LOGO: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidateJWTTokenResponse {
|
type ValidateJWTTokenResponse {
|
||||||
is_valid: Boolean!
|
is_valid: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type GenerateJWTKeysResponse {
|
type GenerateJWTKeysResponse {
|
||||||
secret: String
|
secret: String
|
||||||
public_key: String
|
public_key: String
|
||||||
private_key: String
|
private_key: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type Webhook {
|
type Webhook {
|
||||||
id: ID!
|
id: ID!
|
||||||
event_name: String
|
event_name: String
|
||||||
endpoint: String
|
endpoint: String
|
||||||
enabled: Boolean
|
enabled: Boolean
|
||||||
headers: Map
|
headers: Map
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Webhooks {
|
type Webhooks {
|
||||||
pagination: Pagination!
|
pagination: Pagination!
|
||||||
webhooks: [Webhook!]!
|
webhooks: [Webhook!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebhookLog {
|
type WebhookLog {
|
||||||
id: ID!
|
id: ID!
|
||||||
http_status: Int64
|
http_status: Int64
|
||||||
response: String
|
response: String
|
||||||
request: String
|
request: String
|
||||||
webhook_id: ID
|
webhook_id: ID
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestEndpointResponse {
|
type TestEndpointResponse {
|
||||||
http_status: Int64
|
http_status: Int64
|
||||||
response: String
|
response: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebhookLogs {
|
type WebhookLogs {
|
||||||
pagination: Pagination!
|
pagination: Pagination!
|
||||||
webhook_logs: [WebhookLog!]!
|
webhook_logs: [WebhookLog!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmailTemplate {
|
type EmailTemplate {
|
||||||
id: ID!
|
id: ID!
|
||||||
event_name: String!
|
event_name: String!
|
||||||
template: String!
|
template: String!
|
||||||
subject: String!
|
design: String!
|
||||||
created_at: Int64
|
subject: String!
|
||||||
updated_at: Int64
|
created_at: Int64
|
||||||
|
updated_at: Int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmailTemplates {
|
type EmailTemplates {
|
||||||
pagination: Pagination!
|
pagination: Pagination!
|
||||||
email_templates: [EmailTemplate!]!
|
email_templates: [EmailTemplate!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateEnvInput {
|
input UpdateEnvInput {
|
||||||
ACCESS_TOKEN_EXPIRY_TIME: String
|
ACCESS_TOKEN_EXPIRY_TIME: String
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
CUSTOM_ACCESS_TOKEN_SCRIPT: String
|
||||||
OLD_ADMIN_SECRET: String
|
OLD_ADMIN_SECRET: String
|
||||||
SMTP_HOST: String
|
SMTP_HOST: String
|
||||||
SMTP_PORT: String
|
SMTP_PORT: String
|
||||||
SMTP_USERNAME: String
|
SMTP_USERNAME: String
|
||||||
SMTP_PASSWORD: String
|
SMTP_PASSWORD: String
|
||||||
SENDER_EMAIL: String
|
SENDER_EMAIL: String
|
||||||
JWT_TYPE: String
|
JWT_TYPE: String
|
||||||
JWT_SECRET: String
|
JWT_SECRET: String
|
||||||
JWT_PRIVATE_KEY: String
|
JWT_PRIVATE_KEY: String
|
||||||
JWT_PUBLIC_KEY: String
|
JWT_PUBLIC_KEY: String
|
||||||
ALLOWED_ORIGINS: [String!]
|
ALLOWED_ORIGINS: [String!]
|
||||||
APP_URL: String
|
APP_URL: String
|
||||||
RESET_PASSWORD_URL: String
|
RESET_PASSWORD_URL: String
|
||||||
DISABLE_EMAIL_VERIFICATION: Boolean
|
DISABLE_EMAIL_VERIFICATION: Boolean
|
||||||
DISABLE_BASIC_AUTHENTICATION: Boolean
|
DISABLE_BASIC_AUTHENTICATION: Boolean
|
||||||
DISABLE_MAGIC_LINK_LOGIN: Boolean
|
DISABLE_MAGIC_LINK_LOGIN: Boolean
|
||||||
DISABLE_LOGIN_PAGE: Boolean
|
DISABLE_LOGIN_PAGE: Boolean
|
||||||
DISABLE_SIGN_UP: Boolean
|
DISABLE_SIGN_UP: Boolean
|
||||||
DISABLE_REDIS_FOR_ENV: Boolean
|
DISABLE_REDIS_FOR_ENV: Boolean
|
||||||
DISABLE_STRONG_PASSWORD: Boolean
|
DISABLE_STRONG_PASSWORD: Boolean
|
||||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean
|
DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean
|
||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean
|
||||||
ROLES: [String!]
|
ROLES: [String!]
|
||||||
PROTECTED_ROLES: [String!]
|
PROTECTED_ROLES: [String!]
|
||||||
DEFAULT_ROLES: [String!]
|
DEFAULT_ROLES: [String!]
|
||||||
JWT_ROLE_CLAIM: String
|
JWT_ROLE_CLAIM: String
|
||||||
GOOGLE_CLIENT_ID: String
|
GOOGLE_CLIENT_ID: String
|
||||||
GOOGLE_CLIENT_SECRET: String
|
GOOGLE_CLIENT_SECRET: String
|
||||||
GITHUB_CLIENT_ID: String
|
GITHUB_CLIENT_ID: String
|
||||||
GITHUB_CLIENT_SECRET: String
|
GITHUB_CLIENT_SECRET: String
|
||||||
FACEBOOK_CLIENT_ID: String
|
FACEBOOK_CLIENT_ID: String
|
||||||
FACEBOOK_CLIENT_SECRET: String
|
FACEBOOK_CLIENT_SECRET: String
|
||||||
LINKEDIN_CLIENT_ID: String
|
LINKEDIN_CLIENT_ID: String
|
||||||
LINKEDIN_CLIENT_SECRET: String
|
LINKEDIN_CLIENT_SECRET: String
|
||||||
APPLE_CLIENT_ID: String
|
APPLE_CLIENT_ID: String
|
||||||
APPLE_CLIENT_SECRET: String
|
APPLE_CLIENT_SECRET: String
|
||||||
ORGANIZATION_NAME: String
|
TWITTER_CLIENT_ID: String
|
||||||
ORGANIZATION_LOGO: String
|
TWITTER_CLIENT_SECRET: String
|
||||||
|
ORGANIZATION_NAME: String
|
||||||
|
ORGANIZATION_LOGO: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input AdminLoginInput {
|
input AdminLoginInput {
|
||||||
admin_secret: String!
|
admin_secret: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input AdminSignupInput {
|
input AdminSignupInput {
|
||||||
admin_secret: String!
|
admin_secret: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input SignUpInput {
|
input SignUpInput {
|
||||||
email: String!
|
email: String!
|
||||||
given_name: String
|
given_name: String
|
||||||
family_name: String
|
family_name: String
|
||||||
middle_name: String
|
middle_name: String
|
||||||
nickname: String
|
nickname: String
|
||||||
gender: String
|
gender: String
|
||||||
birthdate: String
|
birthdate: String
|
||||||
phone_number: String
|
phone_number: String
|
||||||
picture: String
|
picture: String
|
||||||
password: String!
|
password: String!
|
||||||
confirm_password: String!
|
confirm_password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
redirect_uri: String
|
redirect_uri: String
|
||||||
is_multi_factor_auth_enabled: Boolean
|
is_multi_factor_auth_enabled: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
input LoginInput {
|
input LoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
password: String!
|
password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input VerifyEmailInput {
|
input VerifyEmailInput {
|
||||||
token: String!
|
token: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResendVerifyEmailInput {
|
input ResendVerifyEmailInput {
|
||||||
email: String!
|
email: String!
|
||||||
identifier: String!
|
identifier: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateProfileInput {
|
input UpdateProfileInput {
|
||||||
old_password: String
|
old_password: String
|
||||||
new_password: String
|
new_password: String
|
||||||
confirm_new_password: String
|
confirm_new_password: String
|
||||||
email: String
|
email: String
|
||||||
given_name: String
|
given_name: String
|
||||||
family_name: String
|
family_name: String
|
||||||
middle_name: String
|
middle_name: String
|
||||||
nickname: String
|
nickname: String
|
||||||
gender: String
|
gender: String
|
||||||
birthdate: String
|
birthdate: String
|
||||||
phone_number: String
|
phone_number: String
|
||||||
picture: String
|
picture: String
|
||||||
is_multi_factor_auth_enabled: Boolean
|
is_multi_factor_auth_enabled: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateUserInput {
|
input UpdateUserInput {
|
||||||
id: ID!
|
id: ID!
|
||||||
email: String
|
email: String
|
||||||
email_verified: Boolean
|
email_verified: Boolean
|
||||||
given_name: String
|
given_name: String
|
||||||
family_name: String
|
family_name: String
|
||||||
middle_name: String
|
middle_name: String
|
||||||
nickname: String
|
nickname: String
|
||||||
gender: String
|
gender: String
|
||||||
birthdate: String
|
birthdate: String
|
||||||
phone_number: String
|
phone_number: String
|
||||||
picture: String
|
picture: String
|
||||||
roles: [String]
|
roles: [String]
|
||||||
is_multi_factor_auth_enabled: Boolean
|
is_multi_factor_auth_enabled: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
input ForgotPasswordInput {
|
input ForgotPasswordInput {
|
||||||
email: String!
|
email: String!
|
||||||
state: String
|
state: String
|
||||||
redirect_uri: String
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResetPasswordInput {
|
input ResetPasswordInput {
|
||||||
token: String!
|
token: String!
|
||||||
password: String!
|
password: String!
|
||||||
confirm_password: String!
|
confirm_password: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteUserInput {
|
input DeleteUserInput {
|
||||||
email: String!
|
email: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input MagicLinkLoginInput {
|
input MagicLinkLoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
state: String
|
state: String
|
||||||
redirect_uri: String
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input SessionQueryInput {
|
input SessionQueryInput {
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input PaginationInput {
|
input PaginationInput {
|
||||||
limit: Int64
|
limit: Int64
|
||||||
page: Int64
|
page: Int64
|
||||||
|
cursor: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input PaginatedInput {
|
input PaginatedInput {
|
||||||
pagination: PaginationInput
|
pagination: PaginationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input OAuthRevokeInput {
|
input OAuthRevokeInput {
|
||||||
refresh_token: String!
|
refresh_token: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input InviteMemberInput {
|
input InviteMemberInput {
|
||||||
emails: [String!]!
|
emails: [String!]!
|
||||||
redirect_uri: String
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateAccessInput {
|
input UpdateAccessInput {
|
||||||
user_id: String!
|
user_id: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input ValidateJWTTokenInput {
|
input ValidateJWTTokenInput {
|
||||||
token_type: String!
|
token_type: String!
|
||||||
token: String!
|
token: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input GenerateJWTKeysInput {
|
input GenerateJWTKeysInput {
|
||||||
type: String!
|
type: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input ListWebhookLogRequest {
|
input ListWebhookLogRequest {
|
||||||
pagination: PaginationInput
|
pagination: PaginationInput
|
||||||
webhook_id: String
|
webhook_id: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input AddWebhookRequest {
|
input AddWebhookRequest {
|
||||||
event_name: String!
|
event_name: String!
|
||||||
endpoint: String!
|
endpoint: String!
|
||||||
enabled: Boolean!
|
enabled: Boolean!
|
||||||
headers: Map
|
headers: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateWebhookRequest {
|
input UpdateWebhookRequest {
|
||||||
id: ID!
|
id: ID!
|
||||||
event_name: String
|
event_name: String
|
||||||
endpoint: String
|
endpoint: String
|
||||||
enabled: Boolean
|
enabled: Boolean
|
||||||
headers: Map
|
headers: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
input WebhookRequest {
|
input WebhookRequest {
|
||||||
id: ID!
|
id: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
input TestEndpointRequest {
|
input TestEndpointRequest {
|
||||||
endpoint: String!
|
endpoint: String!
|
||||||
event_name: String!
|
event_name: String!
|
||||||
headers: Map
|
headers: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
input AddEmailTemplateRequest {
|
input AddEmailTemplateRequest {
|
||||||
event_name: String!
|
event_name: String!
|
||||||
subject: String!
|
subject: String!
|
||||||
template: String!
|
template: String!
|
||||||
|
design: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateEmailTemplateRequest {
|
input UpdateEmailTemplateRequest {
|
||||||
id: ID!
|
id: ID!
|
||||||
event_name: String
|
event_name: String
|
||||||
template: String
|
template: String
|
||||||
subject: String
|
subject: String
|
||||||
|
design: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteEmailTemplateRequest {
|
input DeleteEmailTemplateRequest {
|
||||||
id: ID!
|
id: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
input VerifyOTPRequest {
|
input VerifyOTPRequest {
|
||||||
email: String!
|
email: String!
|
||||||
otp: String!
|
otp: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResendOTPRequest {
|
input ResendOTPRequest {
|
||||||
email: String!
|
email: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
magic_link_login(params: MagicLinkLoginInput!): Response!
|
magic_link_login(params: MagicLinkLoginInput!): Response!
|
||||||
logout: Response!
|
logout: Response!
|
||||||
update_profile(params: UpdateProfileInput!): Response!
|
update_profile(params: UpdateProfileInput!): Response!
|
||||||
verify_email(params: VerifyEmailInput!): AuthResponse!
|
verify_email(params: VerifyEmailInput!): AuthResponse!
|
||||||
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
||||||
forgot_password(params: ForgotPasswordInput!): Response!
|
forgot_password(params: ForgotPasswordInput!): Response!
|
||||||
reset_password(params: ResetPasswordInput!): Response!
|
reset_password(params: ResetPasswordInput!): Response!
|
||||||
revoke(params: OAuthRevokeInput!): Response!
|
revoke(params: OAuthRevokeInput!): Response!
|
||||||
verify_otp(params: VerifyOTPRequest!): AuthResponse!
|
verify_otp(params: VerifyOTPRequest!): AuthResponse!
|
||||||
resend_otp(params: ResendOTPRequest!): Response!
|
resend_otp(params: ResendOTPRequest!): Response!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_delete_user(params: DeleteUserInput!): Response!
|
_delete_user(params: DeleteUserInput!): Response!
|
||||||
_update_user(params: UpdateUserInput!): User!
|
_update_user(params: UpdateUserInput!): User!
|
||||||
_admin_signup(params: AdminSignupInput!): Response!
|
_admin_signup(params: AdminSignupInput!): Response!
|
||||||
_admin_login(params: AdminLoginInput!): Response!
|
_admin_login(params: AdminLoginInput!): Response!
|
||||||
_admin_logout: Response!
|
_admin_logout: Response!
|
||||||
_update_env(params: UpdateEnvInput!): Response!
|
_update_env(params: UpdateEnvInput!): Response!
|
||||||
_invite_members(params: InviteMemberInput!): Response!
|
_invite_members(params: InviteMemberInput!): Response!
|
||||||
_revoke_access(param: UpdateAccessInput!): Response!
|
_revoke_access(param: UpdateAccessInput!): Response!
|
||||||
_enable_access(param: UpdateAccessInput!): Response!
|
_enable_access(param: UpdateAccessInput!): Response!
|
||||||
_generate_jwt_keys(params: GenerateJWTKeysInput!): GenerateJWTKeysResponse!
|
_generate_jwt_keys(params: GenerateJWTKeysInput!): GenerateJWTKeysResponse!
|
||||||
_add_webhook(params: AddWebhookRequest!): Response!
|
_add_webhook(params: AddWebhookRequest!): Response!
|
||||||
_update_webhook(params: UpdateWebhookRequest!): Response!
|
_update_webhook(params: UpdateWebhookRequest!): Response!
|
||||||
_delete_webhook(params: WebhookRequest!): Response!
|
_delete_webhook(params: WebhookRequest!): Response!
|
||||||
_test_endpoint(params: TestEndpointRequest!): TestEndpointResponse!
|
_test_endpoint(params: TestEndpointRequest!): TestEndpointResponse!
|
||||||
_add_email_template(params: AddEmailTemplateRequest!): Response!
|
_add_email_template(params: AddEmailTemplateRequest!): Response!
|
||||||
_update_email_template(params: UpdateEmailTemplateRequest!): Response!
|
_update_email_template(params: UpdateEmailTemplateRequest!): Response!
|
||||||
_delete_email_template(params: DeleteEmailTemplateRequest!): Response!
|
_delete_email_template(params: DeleteEmailTemplateRequest!): Response!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
meta: Meta!
|
meta: Meta!
|
||||||
session(params: SessionQueryInput): AuthResponse!
|
session(params: SessionQueryInput): AuthResponse!
|
||||||
profile: User!
|
profile: User!
|
||||||
validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse!
|
validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_users(params: PaginatedInput): Users!
|
_users(params: PaginatedInput): Users!
|
||||||
_verification_requests(params: PaginatedInput): VerificationRequests!
|
_verification_requests(params: PaginatedInput): VerificationRequests!
|
||||||
_admin_session: Response!
|
_admin_session: Response!
|
||||||
_env: Env!
|
_env: Env!
|
||||||
_webhook(params: WebhookRequest!): Webhook!
|
_webhook(params: WebhookRequest!): Webhook!
|
||||||
_webhooks(params: PaginatedInput): Webhooks!
|
_webhooks(params: PaginatedInput): Webhooks!
|
||||||
_webhook_logs(params: ListWebhookLogRequest): WebhookLogs!
|
_webhook_logs(params: ListWebhookLogRequest): WebhookLogs!
|
||||||
_email_templates(params: PaginatedInput): EmailTemplates!
|
_email_templates(params: PaginatedInput): EmailTemplates!
|
||||||
}
|
}
|
||||||
|
@@ -248,7 +248,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken)
|
||||||
cookie.SetSession(gc, newSessionToken)
|
cookie.SetSession(gc, newSessionToken)
|
||||||
code := uuid.New().String()
|
code := uuid.New().String()
|
||||||
memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken)
|
memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken)
|
||||||
|
@@ -67,6 +67,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
user, err = processLinkedInUserInfo(code)
|
user, err = processLinkedInUserInfo(code)
|
||||||
case constants.AuthRecipeMethodApple:
|
case constants.AuthRecipeMethodApple:
|
||||||
user, err = processAppleUserInfo(code)
|
user, err = processAppleUserInfo(code)
|
||||||
|
case constants.AuthRecipeMethodTwitter:
|
||||||
|
user, err = processTwitterUserInfo(code, sessionState)
|
||||||
default:
|
default:
|
||||||
log.Info("Invalid oauth provider")
|
log.Info("Invalid oauth provider")
|
||||||
err = fmt.Errorf(`invalid oauth provider`)
|
err = fmt.Errorf(`invalid oauth provider`)
|
||||||
@@ -285,9 +287,9 @@ func processGithubUserInfo(code string) (models.User, error) {
|
|||||||
log.Debug("Failed to create github user info request: ", err)
|
log.Debug("Failed to create github user info request: ", err)
|
||||||
return user, fmt.Errorf("error creating github user info request: %s", err.Error())
|
return user, fmt.Errorf("error creating github user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
req.Header = http.Header{
|
req.Header.Set(
|
||||||
"Authorization": []string{fmt.Sprintf("token %s", oauth2Token.AccessToken)},
|
"Authorization", fmt.Sprintf("token %s", oauth2Token.AccessToken),
|
||||||
}
|
)
|
||||||
|
|
||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -329,14 +331,14 @@ func processGithubUserInfo(code string) (models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fetch using /users/email endpoint
|
// fetch using /users/email endpoint
|
||||||
req, err := http.NewRequest("GET", constants.GithubUserEmails, nil)
|
req, err := http.NewRequest(http.MethodGet, constants.GithubUserEmails, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to create github emails request: ", err)
|
log.Debug("Failed to create github emails request: ", err)
|
||||||
return user, fmt.Errorf("error creating github user info request: %s", err.Error())
|
return user, fmt.Errorf("error creating github user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
req.Header = http.Header{
|
req.Header.Set(
|
||||||
"Authorization": []string{fmt.Sprintf("token %s", oauth2Token.AccessToken)},
|
"Authorization", fmt.Sprintf("token %s", oauth2Token.AccessToken),
|
||||||
}
|
)
|
||||||
|
|
||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -564,3 +566,70 @@ func processAppleUserInfo(code string) (models.User, error) {
|
|||||||
|
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func processTwitterUserInfo(code, verifier string) (models.User, error) {
|
||||||
|
user := models.User{}
|
||||||
|
oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(oauth2.NoContext, code, oauth2.SetAuthURLParam("code_verifier", verifier))
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
|
return user, fmt.Errorf("invalid twitter exchange code: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
client := http.Client{}
|
||||||
|
req, err := http.NewRequest("GET", constants.TwitterUserInfoURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to create Twitter user info request: ", err)
|
||||||
|
return user, fmt.Errorf("error creating Twitter user info request: %s", err.Error())
|
||||||
|
}
|
||||||
|
req.Header = http.Header{
|
||||||
|
"Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)},
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to request Twitter user info: ", err)
|
||||||
|
return user, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to read Twitter user info response body: ", err)
|
||||||
|
return user, fmt.Errorf("failed to read Twitter response body: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode >= 400 {
|
||||||
|
log.Debug("Failed to request Twitter user info: ", string(body))
|
||||||
|
return user, fmt.Errorf("failed to request Twitter user info: %s", string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
responseRawData := make(map[string]interface{})
|
||||||
|
json.Unmarshal(body, &responseRawData)
|
||||||
|
|
||||||
|
userRawData := responseRawData["data"].(map[string]interface{})
|
||||||
|
|
||||||
|
// log.Info(userRawData)
|
||||||
|
// Twitter API does not return E-Mail adresses by default. For that case special privileges have
|
||||||
|
// to be granted on a per-App basis. See https://developer.twitter.com/en/docs/twitter-api/v1/accounts-and-users/manage-account-settings/api-reference/get-account-verify_credentials
|
||||||
|
|
||||||
|
// Currently Twitter API only provides the full name of a user. To fill givenName and familyName
|
||||||
|
// the full name will be split at the first whitespace. This approach will not be valid for all name combinations
|
||||||
|
nameArr := strings.SplitAfterN(userRawData["name"].(string), " ", 2)
|
||||||
|
|
||||||
|
firstName := nameArr[0]
|
||||||
|
lastName := ""
|
||||||
|
if len(nameArr) == 2 {
|
||||||
|
lastName = nameArr[1]
|
||||||
|
}
|
||||||
|
nickname := userRawData["username"].(string)
|
||||||
|
profilePicture := userRawData["profile_image_url"].(string)
|
||||||
|
|
||||||
|
user = models.User{
|
||||||
|
GivenName: &firstName,
|
||||||
|
FamilyName: &lastName,
|
||||||
|
Picture: &profilePicture,
|
||||||
|
Nickname: &nickname,
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
"github.com/authorizerdev/authorizer/server/oauth"
|
"github.com/authorizerdev/authorizer/server/oauth"
|
||||||
"github.com/authorizerdev/authorizer/server/parsers"
|
"github.com/authorizerdev/authorizer/server/parsers"
|
||||||
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/authorizerdev/authorizer/server/validators"
|
"github.com/authorizerdev/authorizer/server/validators"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oauthStateString := state + "___" + redirectURI + "___" + roles + "___" + strings.Join(scope, ",")
|
oauthStateString := state + "___" + redirectURI + "___" + roles + "___" + strings.Join(scope, " ")
|
||||||
|
|
||||||
provider := c.Param("oauth_provider")
|
provider := c.Param("oauth_provider")
|
||||||
isProviderConfigured := true
|
isProviderConfigured := true
|
||||||
@@ -169,6 +170,26 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
oauth.OAuthProviders.LinkedInConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodLinkedIn
|
oauth.OAuthProviders.LinkedInConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodLinkedIn
|
||||||
url := oauth.OAuthProviders.LinkedInConfig.AuthCodeURL(oauthStateString)
|
url := oauth.OAuthProviders.LinkedInConfig.AuthCodeURL(oauthStateString)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
|
case constants.AuthRecipeMethodTwitter:
|
||||||
|
if oauth.OAuthProviders.TwitterConfig == nil {
|
||||||
|
log.Debug("Twitter OAuth provider is not configured")
|
||||||
|
isProviderConfigured = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
verifier, challenge := utils.GenerateCodeChallenge()
|
||||||
|
|
||||||
|
err := memorystore.Provider.SetState(oauthStateString, verifier)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error setting state: ", err)
|
||||||
|
c.JSON(500, gin.H{
|
||||||
|
"error": "internal server error",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oauth.OAuthProviders.TwitterConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodTwitter
|
||||||
|
url := oauth.OAuthProviders.TwitterConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("code_challenge", challenge), oauth2.SetAuthURLParam("code_challenge_method", "S256"))
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
case constants.AuthRecipeMethodApple:
|
case constants.AuthRecipeMethodApple:
|
||||||
if oauth.OAuthProviders.AppleConfig == nil {
|
if oauth.OAuthProviders.AppleConfig == nil {
|
||||||
log.Debug("Apple OAuth provider is not configured")
|
log.Debug("Apple OAuth provider is not configured")
|
||||||
|
@@ -76,7 +76,6 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
sessionKey := ""
|
sessionKey := ""
|
||||||
|
|
||||||
if isAuthorizationCodeGrant {
|
if isAuthorizationCodeGrant {
|
||||||
|
|
||||||
if codeVerifier == "" {
|
if codeVerifier == "" {
|
||||||
log.Debug("Code verifier is empty")
|
log.Debug("Code verifier is empty")
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
@@ -134,15 +133,18 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userID = claims.Subject
|
userID = claims.Subject
|
||||||
roles = claims.Roles
|
roles = claims.Roles
|
||||||
scope = claims.Scope
|
scope = claims.Scope
|
||||||
loginMethod = claims.LoginMethod
|
loginMethod = claims.LoginMethod
|
||||||
|
|
||||||
// rollover the session for security
|
// rollover the session for security
|
||||||
sessionKey = userID
|
sessionKey = userID
|
||||||
if loginMethod != "" {
|
if loginMethod != "" {
|
||||||
sessionKey = loginMethod + ":" + userID
|
sessionKey = loginMethod + ":" + userID
|
||||||
}
|
}
|
||||||
|
|
||||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
|
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
|
||||||
} else {
|
} else {
|
||||||
// validate refresh token
|
// validate refresh token
|
||||||
|
@@ -109,6 +109,7 @@ func main() {
|
|||||||
router := routes.InitRouter(log)
|
router := routes.InitRouter(log)
|
||||||
log.Info("Starting Authorizer: ", VERSION)
|
log.Info("Starting Authorizer: ", VERSION)
|
||||||
port, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyPort)
|
port, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyPort)
|
||||||
|
log.Info("Authorizer running at PORT: ", port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Error while getting port from env using default port 8080: ", err)
|
log.Info("Error while getting port from env using default port 8080: ", err)
|
||||||
port = "8080"
|
port = "8080"
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetUserSession sets the user session
|
// SetUserSession sets the user session for given user identifier in form recipe:user_id
|
||||||
func (c *provider) SetUserSession(userId, key, token string) error {
|
func (c *provider) SetUserSession(userId, key, token string) error {
|
||||||
c.sessionStore.Set(userId, key, token)
|
c.sessionStore.Set(userId, key, token)
|
||||||
return nil
|
return nil
|
||||||
@@ -34,6 +34,7 @@ func (c *provider) DeleteAllUserSessions(userId string) error {
|
|||||||
constants.AuthRecipeMethodGithub,
|
constants.AuthRecipeMethodGithub,
|
||||||
constants.AuthRecipeMethodGoogle,
|
constants.AuthRecipeMethodGoogle,
|
||||||
constants.AuthRecipeMethodLinkedIn,
|
constants.AuthRecipeMethodLinkedIn,
|
||||||
|
constants.AuthRecipeMethodTwitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, namespace := range namespaces {
|
for _, namespace := range namespaces {
|
||||||
|
@@ -2,7 +2,7 @@ package providers
|
|||||||
|
|
||||||
// Provider defines current memory store provider
|
// Provider defines current memory store provider
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
// SetUserSession sets the user session
|
// SetUserSession sets the user session for given user identifier in form recipe:user_id
|
||||||
SetUserSession(userId, key, token string) error
|
SetUserSession(userId, key, token string) error
|
||||||
// GetAllUserSessions returns all the user sessions from the session store
|
// GetAllUserSessions returns all the user sessions from the session store
|
||||||
GetAllUserSessions(userId string) (map[string]string, error)
|
GetAllUserSessions(userId string) (map[string]string, error)
|
||||||
|
@@ -14,7 +14,7 @@ var (
|
|||||||
envStorePrefix = "authorizer_env"
|
envStorePrefix = "authorizer_env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetUserSession sets the user session in redis store.
|
// SetUserSession sets the user session for given user identifier in form recipe:user_id
|
||||||
func (c *provider) SetUserSession(userId, key, token string) error {
|
func (c *provider) SetUserSession(userId, key, token string) error {
|
||||||
err := c.store.HSet(c.ctx, userId, key, token).Err()
|
err := c.store.HSet(c.ctx, userId, key, token).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,6 +71,7 @@ func (c *provider) DeleteAllUserSessions(userID string) error {
|
|||||||
constants.AuthRecipeMethodGithub,
|
constants.AuthRecipeMethodGithub,
|
||||||
constants.AuthRecipeMethodGoogle,
|
constants.AuthRecipeMethodGoogle,
|
||||||
constants.AuthRecipeMethodLinkedIn,
|
constants.AuthRecipeMethodLinkedIn,
|
||||||
|
constants.AuthRecipeMethodTwitter,
|
||||||
}
|
}
|
||||||
for _, namespace := range namespaces {
|
for _, namespace := range namespaces {
|
||||||
err := c.store.Del(c.ctx, namespace+":"+userID).Err()
|
err := c.store.Del(c.ctx, namespace+":"+userID).Err()
|
||||||
|
@@ -20,6 +20,7 @@ type OAuthProvider struct {
|
|||||||
FacebookConfig *oauth2.Config
|
FacebookConfig *oauth2.Config
|
||||||
LinkedInConfig *oauth2.Config
|
LinkedInConfig *oauth2.Config
|
||||||
AppleConfig *oauth2.Config
|
AppleConfig *oauth2.Config
|
||||||
|
TwitterConfig *oauth2.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// OIDCProviders is a struct that contains reference all the OpenID providers
|
// OIDCProviders is a struct that contains reference all the OpenID providers
|
||||||
@@ -74,6 +75,7 @@ func InitOAuth() error {
|
|||||||
ClientSecret: githubClientSecret,
|
ClientSecret: githubClientSecret,
|
||||||
RedirectURL: "/oauth_callback/github",
|
RedirectURL: "/oauth_callback/github",
|
||||||
Endpoint: githubOAuth2.Endpoint,
|
Endpoint: githubOAuth2.Endpoint,
|
||||||
|
Scopes: []string{"read:user", "user:email"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,5 +135,28 @@ func InitOAuth() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
twitterClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitterClientID)
|
||||||
|
if err != nil {
|
||||||
|
twitterClientID = ""
|
||||||
|
}
|
||||||
|
twitterClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitterClientSecret)
|
||||||
|
if err != nil {
|
||||||
|
twitterClientSecret = ""
|
||||||
|
}
|
||||||
|
if twitterClientID != "" && twitterClientSecret != "" {
|
||||||
|
OAuthProviders.TwitterConfig = &oauth2.Config{
|
||||||
|
ClientID: twitterClientID,
|
||||||
|
ClientSecret: twitterClientSecret,
|
||||||
|
RedirectURL: "/oauth_callback/twitter",
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
// Endpoint is currently not yet part of oauth2-package. See https://go-review.googlesource.com/c/oauth2/+/350889 for status
|
||||||
|
AuthURL: "https://twitter.com/i/oauth2/authorize",
|
||||||
|
TokenURL: "https://api.twitter.com/2/oauth2/token",
|
||||||
|
AuthStyle: oauth2.AuthStyleInHeader,
|
||||||
|
},
|
||||||
|
Scopes: []string{"tweet.read", "users.read"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -40,10 +40,15 @@ func AddEmailTemplateResolver(ctx context.Context, params model.AddEmailTemplate
|
|||||||
return nil, fmt.Errorf("empty template not allowed")
|
return nil, fmt.Errorf("empty template not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(params.Design) == "" {
|
||||||
|
return nil, fmt.Errorf("empty design not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
_, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{
|
_, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{
|
||||||
EventName: params.EventName,
|
EventName: params.EventName,
|
||||||
Template: params.Template,
|
Template: params.Template,
|
||||||
Subject: params.Subject,
|
Subject: params.Subject,
|
||||||
|
Design: params.Design,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to add email template: ", err)
|
log.Debug("Failed to add email template: ", err)
|
||||||
|
@@ -143,6 +143,13 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
|
|||||||
if val, ok := store[constants.EnvKeyAppleClientSecret]; ok {
|
if val, ok := store[constants.EnvKeyAppleClientSecret]; ok {
|
||||||
res.AppleClientSecret = refs.NewStringRef(val.(string))
|
res.AppleClientSecret = refs.NewStringRef(val.(string))
|
||||||
}
|
}
|
||||||
|
if val, ok := store[constants.EnvKeyTwitterClientID]; ok {
|
||||||
|
res.TwitterClientID = refs.NewStringRef(val.(string))
|
||||||
|
}
|
||||||
|
if val, ok := store[constants.EnvKeyTwitterClientSecret]; ok {
|
||||||
|
res.TwitterClientSecret = refs.NewStringRef(val.(string))
|
||||||
|
}
|
||||||
|
|
||||||
if val, ok := store[constants.EnvKeyOrganizationName]; ok {
|
if val, ok := store[constants.EnvKeyOrganizationName]; ok {
|
||||||
res.OrganizationName = refs.NewStringRef(val.(string))
|
res.OrganizationName = refs.NewStringRef(val.(string))
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
"github.com/authorizerdev/authorizer/server/parsers"
|
"github.com/authorizerdev/authorizer/server/parsers"
|
||||||
|
"github.com/authorizerdev/authorizer/server/refs"
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/authorizerdev/authorizer/server/validators"
|
"github.com/authorizerdev/authorizer/server/validators"
|
||||||
@@ -61,9 +62,9 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||||||
log.Debug("Failed to generate nonce: ", err)
|
log.Debug("Failed to generate nonce: ", err)
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
redirectURL := parsers.GetAppURL(gc) + "/reset-password"
|
redirectURL := parsers.GetAppURL(gc)
|
||||||
if params.RedirectURI != nil {
|
if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" {
|
||||||
redirectURL = *params.RedirectURI
|
redirectURL = refs.StringValue(params.RedirectURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL)
|
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL)
|
||||||
|
@@ -50,7 +50,7 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
|
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to get user by email: ", err)
|
log.Debug("Failed to get user by email: ", err)
|
||||||
return res, fmt.Errorf(`user with this email not found`)
|
return res, fmt.Errorf(`bad user credentials`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.RevokedTimestamp != nil {
|
if user.RevokedTimestamp != nil {
|
||||||
@@ -72,7 +72,7 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to compare password: ", err)
|
log.Debug("Failed to compare password: ", err)
|
||||||
return res, fmt.Errorf(`invalid password`)
|
return res, fmt.Errorf(`bad user credentials`)
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
defaultRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||||
|
@@ -77,6 +77,18 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
|
|||||||
githubClientSecret = ""
|
githubClientSecret = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
twitterClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitterClientID)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get Twitter Client ID from environment variable", err)
|
||||||
|
twitterClientID = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
twitterClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitterClientSecret)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get Twitter Client Secret from environment variable", err)
|
||||||
|
twitterClientSecret = ""
|
||||||
|
}
|
||||||
|
|
||||||
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
|
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to get Disable Basic Authentication from environment variable", err)
|
log.Debug("Failed to get Disable Basic Authentication from environment variable", err)
|
||||||
@@ -121,6 +133,7 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
|
|||||||
IsFacebookLoginEnabled: facebookClientID != "" && facebookClientSecret != "",
|
IsFacebookLoginEnabled: facebookClientID != "" && facebookClientSecret != "",
|
||||||
IsLinkedinLoginEnabled: linkedClientID != "" && linkedInClientSecret != "",
|
IsLinkedinLoginEnabled: linkedClientID != "" && linkedInClientSecret != "",
|
||||||
IsAppleLoginEnabled: appleClientID != "" && appleClientSecret != "",
|
IsAppleLoginEnabled: appleClientID != "" && appleClientSecret != "",
|
||||||
|
IsTwitterLoginEnabled: twitterClientID != "" && twitterClientSecret != "",
|
||||||
IsBasicAuthenticationEnabled: !isBasicAuthDisabled,
|
IsBasicAuthenticationEnabled: !isBasicAuthDisabled,
|
||||||
IsEmailVerificationEnabled: !isEmailVerificationDisabled,
|
IsEmailVerificationEnabled: !isEmailVerificationDisabled,
|
||||||
IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled,
|
IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled,
|
||||||
|
@@ -65,6 +65,14 @@ func UpdateEmailTemplateResolver(ctx context.Context, params model.UpdateEmailTe
|
|||||||
emailTemplateDetails.Template = refs.StringValue(params.Template)
|
emailTemplateDetails.Template = refs.StringValue(params.Template)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.Design != nil && emailTemplateDetails.Design != refs.StringValue(params.Design) {
|
||||||
|
if strings.TrimSpace(refs.StringValue(params.Design)) == "" {
|
||||||
|
log.Debug("empty design not allowed")
|
||||||
|
return nil, fmt.Errorf("empty design not allowed")
|
||||||
|
}
|
||||||
|
emailTemplateDetails.Design = refs.StringValue(params.Design)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = db.Provider.UpdateEmailTemplate(ctx, emailTemplateDetails)
|
_, err = db.Provider.UpdateEmailTemplate(ctx, emailTemplateDetails)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@@ -31,6 +31,7 @@ func clearSessionIfRequired(currentData, updatedData map[string]interface{}) {
|
|||||||
isCurrentGoogleLoginEnabled := currentData[constants.EnvKeyGoogleClientID] != nil && currentData[constants.EnvKeyGoogleClientSecret] != nil && currentData[constants.EnvKeyGoogleClientID].(string) != "" && currentData[constants.EnvKeyGoogleClientSecret].(string) != ""
|
isCurrentGoogleLoginEnabled := currentData[constants.EnvKeyGoogleClientID] != nil && currentData[constants.EnvKeyGoogleClientSecret] != nil && currentData[constants.EnvKeyGoogleClientID].(string) != "" && currentData[constants.EnvKeyGoogleClientSecret].(string) != ""
|
||||||
isCurrentGithubLoginEnabled := currentData[constants.EnvKeyGithubClientID] != nil && currentData[constants.EnvKeyGithubClientSecret] != nil && currentData[constants.EnvKeyGithubClientID].(string) != "" && currentData[constants.EnvKeyGithubClientSecret].(string) != ""
|
isCurrentGithubLoginEnabled := currentData[constants.EnvKeyGithubClientID] != nil && currentData[constants.EnvKeyGithubClientSecret] != nil && currentData[constants.EnvKeyGithubClientID].(string) != "" && currentData[constants.EnvKeyGithubClientSecret].(string) != ""
|
||||||
isCurrentLinkedInLoginEnabled := currentData[constants.EnvKeyLinkedInClientID] != nil && currentData[constants.EnvKeyLinkedInClientSecret] != nil && currentData[constants.EnvKeyLinkedInClientID].(string) != "" && currentData[constants.EnvKeyLinkedInClientSecret].(string) != ""
|
isCurrentLinkedInLoginEnabled := currentData[constants.EnvKeyLinkedInClientID] != nil && currentData[constants.EnvKeyLinkedInClientSecret] != nil && currentData[constants.EnvKeyLinkedInClientID].(string) != "" && currentData[constants.EnvKeyLinkedInClientSecret].(string) != ""
|
||||||
|
isCurrentTwitterLoginEnabled := currentData[constants.EnvKeyTwitterClientID] != nil && currentData[constants.EnvKeyTwitterClientSecret] != nil && currentData[constants.EnvKeyTwitterClientID].(string) != "" && currentData[constants.EnvKeyTwitterClientSecret].(string) != ""
|
||||||
|
|
||||||
isUpdatedBasicAuthEnabled := !updatedData[constants.EnvKeyDisableBasicAuthentication].(bool)
|
isUpdatedBasicAuthEnabled := !updatedData[constants.EnvKeyDisableBasicAuthentication].(bool)
|
||||||
isUpdatedMagicLinkLoginEnabled := !updatedData[constants.EnvKeyDisableMagicLinkLogin].(bool)
|
isUpdatedMagicLinkLoginEnabled := !updatedData[constants.EnvKeyDisableMagicLinkLogin].(bool)
|
||||||
@@ -39,6 +40,7 @@ func clearSessionIfRequired(currentData, updatedData map[string]interface{}) {
|
|||||||
isUpdatedGoogleLoginEnabled := updatedData[constants.EnvKeyGoogleClientID] != nil && updatedData[constants.EnvKeyGoogleClientSecret] != nil && updatedData[constants.EnvKeyGoogleClientID].(string) != "" && updatedData[constants.EnvKeyGoogleClientSecret].(string) != ""
|
isUpdatedGoogleLoginEnabled := updatedData[constants.EnvKeyGoogleClientID] != nil && updatedData[constants.EnvKeyGoogleClientSecret] != nil && updatedData[constants.EnvKeyGoogleClientID].(string) != "" && updatedData[constants.EnvKeyGoogleClientSecret].(string) != ""
|
||||||
isUpdatedGithubLoginEnabled := updatedData[constants.EnvKeyGithubClientID] != nil && updatedData[constants.EnvKeyGithubClientSecret] != nil && updatedData[constants.EnvKeyGithubClientID].(string) != "" && updatedData[constants.EnvKeyGithubClientSecret].(string) != ""
|
isUpdatedGithubLoginEnabled := updatedData[constants.EnvKeyGithubClientID] != nil && updatedData[constants.EnvKeyGithubClientSecret] != nil && updatedData[constants.EnvKeyGithubClientID].(string) != "" && updatedData[constants.EnvKeyGithubClientSecret].(string) != ""
|
||||||
isUpdatedLinkedInLoginEnabled := updatedData[constants.EnvKeyLinkedInClientID] != nil && updatedData[constants.EnvKeyLinkedInClientSecret] != nil && updatedData[constants.EnvKeyLinkedInClientID].(string) != "" && updatedData[constants.EnvKeyLinkedInClientSecret].(string) != ""
|
isUpdatedLinkedInLoginEnabled := updatedData[constants.EnvKeyLinkedInClientID] != nil && updatedData[constants.EnvKeyLinkedInClientSecret] != nil && updatedData[constants.EnvKeyLinkedInClientID].(string) != "" && updatedData[constants.EnvKeyLinkedInClientSecret].(string) != ""
|
||||||
|
isUpdatedTwitterLoginEnabled := updatedData[constants.EnvKeyTwitterClientID] != nil && updatedData[constants.EnvKeyTwitterClientSecret] != nil && updatedData[constants.EnvKeyTwitterClientID].(string) != "" && updatedData[constants.EnvKeyTwitterClientSecret].(string) != ""
|
||||||
|
|
||||||
if isCurrentBasicAuthEnabled && !isUpdatedBasicAuthEnabled {
|
if isCurrentBasicAuthEnabled && !isUpdatedBasicAuthEnabled {
|
||||||
memorystore.Provider.DeleteSessionForNamespace(constants.AuthRecipeMethodBasicAuth)
|
memorystore.Provider.DeleteSessionForNamespace(constants.AuthRecipeMethodBasicAuth)
|
||||||
@@ -67,6 +69,10 @@ func clearSessionIfRequired(currentData, updatedData map[string]interface{}) {
|
|||||||
if isCurrentLinkedInLoginEnabled && !isUpdatedLinkedInLoginEnabled {
|
if isCurrentLinkedInLoginEnabled && !isUpdatedLinkedInLoginEnabled {
|
||||||
memorystore.Provider.DeleteSessionForNamespace(constants.AuthRecipeMethodLinkedIn)
|
memorystore.Provider.DeleteSessionForNamespace(constants.AuthRecipeMethodLinkedIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isCurrentTwitterLoginEnabled && !isUpdatedTwitterLoginEnabled {
|
||||||
|
memorystore.Provider.DeleteSessionForNamespace(constants.AuthRecipeMethodTwitter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateEnvResolver is a resolver for update config mutation
|
// UpdateEnvResolver is a resolver for update config mutation
|
||||||
|
@@ -245,7 +245,7 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// exec it as go routine so that we can reduce the api latency
|
// exec it as go routine so that we can reduce the api latency
|
||||||
go email.SendEmail([]string{user.Email}, constants.VerificationTypeBasicAuthSignup, map[string]interface{}{
|
go email.SendEmail([]string{user.Email}, verificationType, map[string]interface{}{
|
||||||
"user": user.ToMap(),
|
"user": user.ToMap(),
|
||||||
"organization": utils.GetOrganization(),
|
"organization": utils.GetOrganization(),
|
||||||
"verification_url": utils.GetEmailVerificationURL(verificationToken, hostname),
|
"verification_url": utils.GetEmailVerificationURL(verificationToken, hostname),
|
||||||
|
@@ -31,7 +31,7 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
assert.Nil(t, emailTemplate)
|
assert.Nil(t, emailTemplate)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should not add email template for empty template", func(t *testing.T) {
|
t.Run("should not add email template for empty subject", func(t *testing.T) {
|
||||||
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
||||||
EventName: s.TestInfo.TestEmailTemplateEventTypes[0],
|
EventName: s.TestInfo.TestEmailTemplateEventTypes[0],
|
||||||
Template: " test ",
|
Template: " test ",
|
||||||
@@ -50,12 +50,25 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Nil(t, emailTemplate)
|
assert.Nil(t, emailTemplate)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should not add email template with empty design", func(t *testing.T) {
|
||||||
|
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
||||||
|
EventName: s.TestInfo.TestEmailTemplateEventTypes[0],
|
||||||
|
Template: "test",
|
||||||
|
Subject: "test",
|
||||||
|
Design: " ",
|
||||||
|
})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, emailTemplate)
|
||||||
|
})
|
||||||
|
|
||||||
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
||||||
t.Run("should add email template for "+eventType, func(t *testing.T) {
|
t.Run("should add email template for "+eventType, func(t *testing.T) {
|
||||||
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
||||||
EventName: eventType,
|
EventName: eventType,
|
||||||
Template: "Test email",
|
Template: "Test email",
|
||||||
Subject: "Test email",
|
Subject: "Test email",
|
||||||
|
Design: "Test design",
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, emailTemplate)
|
assert.NotNil(t, emailTemplate)
|
||||||
@@ -65,6 +78,7 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, et.EventName, eventType)
|
assert.Equal(t, et.EventName, eventType)
|
||||||
assert.Equal(t, "Test email", et.Subject)
|
assert.Equal(t, "Test email", et.Subject)
|
||||||
|
assert.Equal(t, "Test design", et.Design)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -32,6 +32,7 @@ func updateEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
ID: emailTemplate.ID,
|
ID: emailTemplate.ID,
|
||||||
Template: refs.NewStringRef("Updated test template"),
|
Template: refs.NewStringRef("Updated test template"),
|
||||||
Subject: refs.NewStringRef("Updated subject"),
|
Subject: refs.NewStringRef("Updated subject"),
|
||||||
|
Design: refs.NewStringRef("Updated design"),
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -44,5 +45,6 @@ func updateEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
assert.Equal(t, emailTemplate.ID, updatedEmailTemplate.ID)
|
assert.Equal(t, emailTemplate.ID, updatedEmailTemplate.ID)
|
||||||
assert.Equal(t, updatedEmailTemplate.Template, "Updated test template")
|
assert.Equal(t, updatedEmailTemplate.Template, "Updated test template")
|
||||||
assert.Equal(t, updatedEmailTemplate.Subject, "Updated subject")
|
assert.Equal(t, updatedEmailTemplate.Subject, "Updated subject")
|
||||||
|
assert.Equal(t, updatedEmailTemplate.Design, "Updated design")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -298,7 +298,6 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
|
|||||||
if res.LoginMethod != "" {
|
if res.LoginMethod != "" {
|
||||||
sessionStoreKey = res.LoginMethod + ":" + res.Subject
|
sessionStoreKey = res.LoginMethod + ":" + res.Subject
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := memorystore.Provider.GetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+res.Nonce)
|
token, err := memorystore.Provider.GetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+res.Nonce)
|
||||||
if token == "" || err != nil {
|
if token == "" || err != nil {
|
||||||
log.Debug("invalid browser session:", err)
|
log.Debug("invalid browser session:", err)
|
||||||
|
32
server/utils/pkce.go
Normal file
32
server/utils/pkce.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
b64 "encoding/base64"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
length = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateCodeChallenge creates PKCE-Code-Challenge
|
||||||
|
// and returns the verifier and challenge
|
||||||
|
func GenerateCodeChallenge() (string, string) {
|
||||||
|
// Generate Verifier
|
||||||
|
randGenerator := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
randomBytes := make([]byte, length)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
randomBytes[i] = byte(randGenerator.Intn(255))
|
||||||
|
}
|
||||||
|
verifier := strings.Trim(b64.URLEncoding.EncodeToString(randomBytes), "=")
|
||||||
|
|
||||||
|
// Generate Challenge
|
||||||
|
rawChallenge := sha256.New()
|
||||||
|
rawChallenge.Write([]byte(verifier))
|
||||||
|
challenge := strings.Trim(b64.URLEncoding.EncodeToString(rawChallenge.Sum(nil)), "=")
|
||||||
|
|
||||||
|
return verifier, challenge
|
||||||
|
}
|
@@ -30,8 +30,8 @@ func IsValidOrigin(url string) bool {
|
|||||||
replacedString := origin
|
replacedString := origin
|
||||||
// if has regex whitelisted domains
|
// if has regex whitelisted domains
|
||||||
if strings.Contains(origin, "*") {
|
if strings.Contains(origin, "*") {
|
||||||
replacedString = strings.Replace(origin, ".", "\\.", -1)
|
replacedString = strings.ReplaceAll(origin, ".", "\\.")
|
||||||
replacedString = strings.Replace(replacedString, "*", ".*", -1)
|
replacedString = strings.ReplaceAll(replacedString, "*", ".*")
|
||||||
|
|
||||||
if strings.HasPrefix(replacedString, ".*") {
|
if strings.HasPrefix(replacedString, ".*") {
|
||||||
replacedString += "\\b"
|
replacedString += "\\b"
|
||||||
|
Reference in New Issue
Block a user