Compare commits
164 Commits
fix-db-ref
...
2420297c20
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2420297c20 | ||
![]() |
853f8c44bb | ||
bdcf2c39f7 | |||
c53ada95eb | |||
07a26991e9 | |||
c8413665ae | |||
69a87896e9 | |||
![]() |
27b51ad369 | ||
![]() |
747c82f1b9 | ||
![]() |
b1bfaf6688 | ||
034d80303f | |||
![]() |
a328121aa3 | ||
![]() |
40a0a2fbcc | ||
![]() |
e52164665f | ||
![]() |
a63d00b0c8 | ||
![]() |
0bce901749 | ||
![]() |
e5fbaa26e1 | ||
bb62b4adfd | |||
d47b39bb1d | |||
0aa1c2532b | |||
0ab26c19b6 | |||
b8aade7dc0 | |||
6e0ab799b8 | |||
2ccc2ae4eb | |||
95807ae319 | |||
e62f356a79 | |||
0331d34afc | |||
5af71dfc94 | |||
dd4b41674a | |||
58a91814ae | |||
2fba6af769 | |||
ee454aeabe | |||
896e890421 | |||
c5aaad0662 | |||
68c761a181 | |||
f30881de3d | |||
534b5624af | |||
be88e231e0 | |||
1f3cb1aab9 | |||
![]() |
cb01dea902 | ||
![]() |
7bcd5a70c3 | ||
![]() |
3bd3a52d3b | ||
![]() |
ade676f92c | ||
![]() |
ca71aba96d | ||
![]() |
ef2a590608 | ||
![]() |
59ed4e273f | ||
![]() |
751933d40e | ||
![]() |
5fac440205 | ||
![]() |
5ba30ccd12 | ||
![]() |
02f7a62918 | ||
![]() |
1172c95a23 | ||
![]() |
c9c2789097 | ||
![]() |
ed3100c179 | ||
![]() |
b8c6ee0a6d | ||
![]() |
0a5357c948 | ||
![]() |
a002e2faf7 | ||
![]() |
d8b9ffe9ce | ||
![]() |
47f26103b0 | ||
![]() |
48ada9ab26 | ||
![]() |
b8c2ab4cf8 | ||
![]() |
5cb94a7820 | ||
![]() |
7e9fac335b | ||
![]() |
febf4f9b15 | ||
![]() |
b0f54b181d | ||
![]() |
df5978fb5a | ||
![]() |
2a0e0da436 | ||
![]() |
e4a8eb3542 | ||
![]() |
06214f0b1d | ||
![]() |
a203b853f2 | ||
![]() |
109b38e588 | ||
![]() |
e8b99f73c3 | ||
![]() |
3fa892431e | ||
![]() |
4b341c0a5d | ||
![]() |
c95db8b07b | ||
![]() |
32fcba0f8d | ||
![]() |
5b75521490 | ||
![]() |
f70310f04f | ||
![]() |
bea6eb8342 | ||
![]() |
cac67b7915 | ||
![]() |
d7da81d308 | ||
![]() |
e49e315967 | ||
![]() |
fbb4975c02 | ||
![]() |
7f6ddca3fc | ||
![]() |
e71da3def6 | ||
![]() |
46d6f86ab0 | ||
![]() |
1890db8f03 | ||
![]() |
0e96e0b6f0 | ||
![]() |
bbddf484ed | ||
![]() |
7d4c641297 | ||
![]() |
bd343f0b27 | ||
![]() |
ad8bd64987 | ||
![]() |
de5c18b60f | ||
![]() |
fe4c693324 | ||
![]() |
d8cd965004 | ||
![]() |
fdd41721a8 | ||
![]() |
281714b86d | ||
![]() |
a05f5ce063 | ||
![]() |
b7627a36a6 | ||
![]() |
85bbd1223e | ||
![]() |
885a147463 | ||
![]() |
9a6f1a659a | ||
![]() |
4bddbde280 | ||
![]() |
3ed31b0557 | ||
![]() |
4d1fcc3004 | ||
![]() |
99441964b5 | ||
![]() |
be3d8cf69b | ||
![]() |
734e54db69 | ||
![]() |
a3bda429d0 | ||
![]() |
3fba4e4c28 | ||
![]() |
2a759de311 | ||
![]() |
e7c4ee5630 | ||
![]() |
6a4568dcf3 | ||
![]() |
e941e4834a | ||
![]() |
f906fb74af | ||
![]() |
7ced811e6e | ||
![]() |
c1e1ee13f2 | ||
![]() |
60de61a74e | ||
![]() |
843bc022fe | ||
![]() |
ad41bcf792 | ||
![]() |
ea2596b9ae | ||
![]() |
c8ccb89a67 | ||
![]() |
9519b53d4e | ||
![]() |
e7cfaf4fbe | ||
![]() |
0428488dab | ||
![]() |
f3b672a4cf | ||
![]() |
1d6f569f92 | ||
![]() |
fbdc24f314 | ||
![]() |
1275644abe | ||
![]() |
ac6b08c093 | ||
![]() |
fe461b675b | ||
![]() |
77e219d990 | ||
![]() |
3ea13d7e93 | ||
![]() |
940a09d172 | ||
![]() |
3ac6875f87 | ||
![]() |
937506ff64 | ||
![]() |
9795ba9097 | ||
![]() |
11dbe9d97a | ||
![]() |
c9b8bbc3e1 | ||
![]() |
a124edfaee | ||
![]() |
5e6b033024 | ||
![]() |
171d4e3fff | ||
![]() |
cf96a0087f | ||
![]() |
09cfad9c27 | ||
![]() |
35e563ab3b | ||
![]() |
e625ed9633 | ||
![]() |
a042c202a0 | ||
![]() |
7a76b783b1 | ||
![]() |
e5400bc7bd | ||
![]() |
a8503666e3 | ||
![]() |
b028be3cbc | ||
![]() |
9a8d20b698 | ||
![]() |
fab3c2f87e | ||
![]() |
0c334856bc | ||
![]() |
ba0cf189de | ||
![]() |
9f52c08883 | ||
![]() |
80f3698f06 | ||
![]() |
2a2b7abc08 | ||
![]() |
27e3ed82e4 | ||
![]() |
6077702626 | ||
![]() |
cf54fcef03 | ||
![]() |
2f849b8f0c | ||
![]() |
85ca0f09bf | ||
![]() |
e7652db89c | ||
![]() |
5018462559 |
@@ -1,4 +1,5 @@
|
|||||||
ENV=production
|
ENV=production
|
||||||
DATABASE_URL=data.db
|
DATABASE_URL=data.db
|
||||||
DATABASE_TYPE=sqlite
|
DATABASE_TYPE=sqlite
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
|
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
|
||||||
|
DISABLE_PLAYGROUND=true
|
||||||
|
@@ -12,4 +12,4 @@ TWILIO_API_SECRET=test
|
|||||||
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||||
TWILIO_SENDER=909921212112
|
TWILIO_SENDER=909921212112
|
||||||
SENDER_NAME="Authorizer"
|
SENDER_NAME="Authorizer"
|
||||||
AWS_REGION=ap-south-1
|
AWS_REGION=ap-south-1
|
||||||
|
35
.gitea/workflows/main.yml
Normal file
35
.gitea/workflows/main.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: 'deploy'
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Cloning repo
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Get Repo Name
|
||||||
|
id: repo_name
|
||||||
|
run: echo "::set-output name=repo::$(echo ${GITHUB_REPOSITORY##*/})"
|
||||||
|
|
||||||
|
- name: Get Branch Name
|
||||||
|
id: branch_name
|
||||||
|
run: echo "::set-output name=branch::$(echo ${GITHUB_REF##*/})"
|
||||||
|
|
||||||
|
- name: Push to dokku for main branch
|
||||||
|
if: steps.branch_name.outputs.branch == 'mailgun'
|
||||||
|
uses: dokku/github-action@master
|
||||||
|
with:
|
||||||
|
branch: 'main'
|
||||||
|
git_remote_url: 'ssh://dokku@v2.discours.io:22/authorizer'
|
||||||
|
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- name: Push to dokku for dev branch
|
||||||
|
if: steps.branch_name.outputs.branch == 'dev'
|
||||||
|
uses: dokku/github-action@master
|
||||||
|
with:
|
||||||
|
branch: 'main'
|
||||||
|
git_remote_url: 'ssh://dokku@staging.discours.io:22/authorizer'
|
||||||
|
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -62,12 +62,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
make clean && \
|
make clean && \
|
||||||
make build && \
|
make build && \
|
||||||
|
mkdir -p authorizer-${VERSION}-darwin-arm64/build authorizer-${VERSION}-darwin-arm64/app authorizer-${VERSION}-darwin-arm64/dashboard && cp build/darwin/arm64/server authorizer-${VERSION}-darwin-arm64/build/ && cp .env authorizer-${VERSION}-darwin-arm64/.env && cp -rf app/build authorizer-${VERSION}-darwin-arm64/app/build && cp -rf templates authorizer-${VERSION}-darwin-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-arm64.tar.gz authorizer-${VERSION}-darwin-arm64 && \
|
||||||
mkdir -p authorizer-${VERSION}-darwin-amd64/build authorizer-${VERSION}-darwin-amd64/app authorizer-${VERSION}-darwin-amd64/dashboard && cp build/darwin/amd64/server authorizer-${VERSION}-darwin-amd64/build/ && cp .env authorizer-${VERSION}-darwin-amd64/.env && cp -rf app/build authorizer-${VERSION}-darwin-amd64/app/build && cp -rf templates authorizer-${VERSION}-darwin-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-amd64.tar.gz authorizer-${VERSION}-darwin-amd64 && \
|
mkdir -p authorizer-${VERSION}-darwin-amd64/build authorizer-${VERSION}-darwin-amd64/app authorizer-${VERSION}-darwin-amd64/dashboard && cp build/darwin/amd64/server authorizer-${VERSION}-darwin-amd64/build/ && cp .env authorizer-${VERSION}-darwin-amd64/.env && cp -rf app/build authorizer-${VERSION}-darwin-amd64/app/build && cp -rf templates authorizer-${VERSION}-darwin-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-amd64.tar.gz authorizer-${VERSION}-darwin-amd64 && \
|
||||||
mkdir -p authorizer-${VERSION}-linux-amd64/build authorizer-${VERSION}-linux-amd64/app authorizer-${VERSION}-linux-amd64/dashboard && cp build/linux/amd64/server authorizer-${VERSION}-linux-amd64/build/ && cp .env authorizer-${VERSION}-linux-amd64/.env && cp -rf app/build authorizer-${VERSION}-linux-amd64/app/build && cp -rf templates authorizer-${VERSION}-linux-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz authorizer-${VERSION}-linux-amd64 && \
|
mkdir -p authorizer-${VERSION}-linux-amd64/build authorizer-${VERSION}-linux-amd64/app authorizer-${VERSION}-linux-amd64/dashboard && cp build/linux/amd64/server authorizer-${VERSION}-linux-amd64/build/ && cp .env authorizer-${VERSION}-linux-amd64/.env && cp -rf app/build authorizer-${VERSION}-linux-amd64/app/build && cp -rf templates authorizer-${VERSION}-linux-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz authorizer-${VERSION}-linux-amd64 && \
|
||||||
mkdir -p authorizer-${VERSION}-linux-arm64/build authorizer-${VERSION}-linux-arm64/app authorizer-${VERSION}-linux-arm64/dashboard && cp build/linux/arm64/server authorizer-${VERSION}-linux-arm64/build/ && cp .env authorizer-${VERSION}-linux-arm64/.env && cp -rf app/build authorizer-${VERSION}-linux-arm64/app/build && cp -rf templates authorizer-${VERSION}-linux-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-arm64.tar.gz authorizer-${VERSION}-linux-arm64 && \
|
mkdir -p authorizer-${VERSION}-linux-arm64/build authorizer-${VERSION}-linux-arm64/app authorizer-${VERSION}-linux-arm64/dashboard && cp build/linux/arm64/server authorizer-${VERSION}-linux-arm64/build/ && cp .env authorizer-${VERSION}-linux-arm64/.env && cp -rf app/build authorizer-${VERSION}-linux-arm64/app/build && cp -rf templates authorizer-${VERSION}-linux-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-arm64.tar.gz authorizer-${VERSION}-linux-arm64 && \
|
||||||
mkdir -p authorizer-${VERSION}-windows-amd64/build authorizer-${VERSION}-windows-amd64/app authorizer-${VERSION}-windows-amd64/dashboard && cp build/windows/amd64/server.exe authorizer-${VERSION}-windows-amd64/build/ && cp .env authorizer-${VERSION}-windows-amd64/.env && cp -rf app/build authorizer-${VERSION}-windows-amd64/app/build && cp -rf templates authorizer-${VERSION}-windows-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-windows-amd64/dashboard/build && zip -vr authorizer-${VERSION}-windows-amd64.zip authorizer-${VERSION}-windows-amd64
|
mkdir -p authorizer-${VERSION}-windows-amd64/build authorizer-${VERSION}-windows-amd64/app authorizer-${VERSION}-windows-amd64/dashboard && cp build/windows/amd64/server.exe authorizer-${VERSION}-windows-amd64/build/ && cp .env authorizer-${VERSION}-windows-amd64/.env && cp -rf app/build authorizer-${VERSION}-windows-amd64/app/build && cp -rf templates authorizer-${VERSION}-windows-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-windows-amd64/dashboard/build && zip -vr authorizer-${VERSION}-windows-amd64.zip authorizer-${VERSION}-windows-amd64
|
||||||
- name: Upload assets
|
- name: Upload assets
|
||||||
run: |
|
run: |
|
||||||
|
github-assets-uploader -f authorizer-${VERSION}-darwin-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||||
github-assets-uploader -f authorizer-${VERSION}-darwin-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
github-assets-uploader -f authorizer-${VERSION}-darwin-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||||
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||||
github-assets-uploader -f authorizer-${VERSION}-linux-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
github-assets-uploader -f authorizer-${VERSION}-linux-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,4 +17,6 @@ test.db
|
|||||||
yalc.lock
|
yalc.lock
|
||||||
certs/
|
certs/
|
||||||
*-shm
|
*-shm
|
||||||
*-wal
|
*-wal
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.19.5-alpine as go-builder
|
FROM golang:1.21.3-alpine3.18 as go-builder
|
||||||
WORKDIR /authorizer
|
WORKDIR /authorizer
|
||||||
COPY server server
|
COPY server server
|
||||||
COPY Makefile .
|
COPY Makefile .
|
||||||
@@ -11,7 +11,7 @@ RUN apk add build-base &&\
|
|||||||
make clean && make && \
|
make clean && make && \
|
||||||
chmod 777 build/server
|
chmod 777 build/server
|
||||||
|
|
||||||
FROM node:17-alpine3.12 as node-builder
|
FROM node:20-alpine3.18 as node-builder
|
||||||
WORKDIR /authorizer
|
WORKDIR /authorizer
|
||||||
COPY app app
|
COPY app app
|
||||||
COPY dashboard dashboard
|
COPY dashboard dashboard
|
||||||
@@ -20,7 +20,7 @@ RUN apk add build-base &&\
|
|||||||
make build-app && \
|
make build-app && \
|
||||||
make build-dashboard
|
make build-dashboard
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:3.18
|
||||||
RUN adduser -D -h /authorizer -u 1000 -k /dev/null authorizer
|
RUN adduser -D -h /authorizer -u 1000 -k /dev/null authorizer
|
||||||
WORKDIR /authorizer
|
WORKDIR /authorizer
|
||||||
RUN mkdir app dashboard
|
RUN mkdir app dashboard
|
||||||
|
8
Makefile
8
Makefile
@@ -5,7 +5,7 @@ cmd:
|
|||||||
cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server'
|
cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server'
|
||||||
build:
|
build:
|
||||||
cd server && gox \
|
cd server && gox \
|
||||||
-osarch="linux/amd64 linux/arm64 darwin/amd64 windows/amd64" \
|
-osarch="linux/amd64 linux/arm64 darwin/arm64 darwin/amd64 windows/amd64" \
|
||||||
-ldflags "-w -X main.VERSION=$(VERSION)" \
|
-ldflags "-w -X main.VERSION=$(VERSION)" \
|
||||||
-output="../build/{{.OS}}/{{.Arch}}/server" \
|
-output="../build/{{.OS}}/{{.Arch}}/server" \
|
||||||
./...
|
./...
|
||||||
@@ -30,7 +30,7 @@ test-arangodb:
|
|||||||
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
|
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
|
||||||
docker rm -vf authorizer_arangodb
|
docker rm -vf authorizer_arangodb
|
||||||
test-dynamodb:
|
test-dynamodb:
|
||||||
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
|
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
|
||||||
cd server && go clean --testcache && TEST_DBS="dynamodb" go test -p 1 -v ./test
|
cd server && go clean --testcache && TEST_DBS="dynamodb" go test -p 1 -v ./test
|
||||||
docker rm -vf dynamodb-local-test
|
docker rm -vf dynamodb-local-test
|
||||||
test-couchbase:
|
test-couchbase:
|
||||||
@@ -46,7 +46,7 @@ test-all-db:
|
|||||||
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
|
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
|
||||||
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
|
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
|
||||||
sh scripts/couchbase-test.sh
|
sh scripts/couchbase-test.sh
|
||||||
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb,dynamodb" go test -p 1 -v ./test
|
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb,dynamodb,couchbase" go test -p 1 -v ./test
|
||||||
docker rm -vf authorizer_scylla_db
|
docker rm -vf authorizer_scylla_db
|
||||||
docker rm -vf authorizer_mongodb_db
|
docker rm -vf authorizer_mongodb_db
|
||||||
docker rm -vf authorizer_arangodb
|
docker rm -vf authorizer_arangodb
|
||||||
@@ -56,4 +56,4 @@ generate-graphql:
|
|||||||
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
|
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
|
||||||
generate-db-template:
|
generate-db-template:
|
||||||
cp -rf server/db/providers/provider_template server/db/providers/${dbname}
|
cp -rf server/db/providers/provider_template server/db/providers/${dbname}
|
||||||
find server/db/providers/${dbname} -type f -exec sed -i -e 's/provider_template/${dbname}/g' {} \;
|
find server/db/providers/${dbname} -type f -exec sed -i -e 's/provider_template/${dbname}/g' {} \;
|
||||||
|
@@ -68,6 +68,8 @@ Deploy production ready Authorizer instance using one click deployment options a
|
|||||||
| 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) |
|
| 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) |
|
||||||
|
| Koyeb | <a target="_blank" href="https://app.koyeb.com/deploy?name=authorizer&type=docker&image=docker.io/lakhansamani/authorizer&env[PORT]=8000&env[DATABASE_TYPE]=postgres&env[DATABASE_URL]=CHANGE_ME&ports=8000;http;/"><img alt="Deploy to Koyeb" src="https://www.koyeb.com/static/images/deploy/button.svg" /></a> | [docs](https://docs.authorizer.dev/deployment/koyeb) |
|
||||||
|
| RepoCloud | <a href="https://repocloud.io/details/?app_id=174"><img src="https://d16t0pc4846x52.cloudfront.net/deploy.png" alt="Deploy on RepoCloud"></a> | [docs](https://repocloud.io/details/?app_id=174) |
|
||||||
|
|
||||||
### Deploy Authorizer Using Source Code
|
### Deploy Authorizer Using Source Code
|
||||||
|
|
||||||
|
875
app/package-lock.json
generated
875
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
|||||||
"author": "Lakhan Samani",
|
"author": "Lakhan Samani",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "^1.1.13",
|
"@authorizerdev/authorizer-react": "^1.2.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",
|
||||||
|
@@ -27,13 +27,13 @@ export default function App() {
|
|||||||
if (redirectURL) {
|
if (redirectURL) {
|
||||||
urlProps.redirectURL = redirectURL;
|
urlProps.redirectURL = redirectURL;
|
||||||
} else {
|
} else {
|
||||||
urlProps.redirectURL = window.location.origin + '/app';
|
urlProps.redirectURL = window.location.href;
|
||||||
}
|
}
|
||||||
const globalState: Record<string, string> = {
|
const globalState: Record<string, string> = {
|
||||||
...window['__authorizer__'],
|
...window['__authorizer__'],
|
||||||
...urlProps,
|
...urlProps,
|
||||||
};
|
};
|
||||||
|
console.log({ globalState });
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -54,7 +54,7 @@ export default function App() {
|
|||||||
<img
|
<img
|
||||||
src={`${globalState.organizationLogo}`}
|
src={`${globalState.organizationLogo}`}
|
||||||
alt="logo"
|
alt="logo"
|
||||||
style={{ height: 60, width: 60, objectFit: 'cover' }}
|
style={{ height: 60, objectFit: 'cover' }}
|
||||||
/>
|
/>
|
||||||
<h1>{globalState.organizationName}</h1>
|
<h1>{globalState.organizationName}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -59,7 +59,9 @@ export default function Root({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token) {
|
if (token) {
|
||||||
let redirectURL = config.redirectURL || '/app';
|
let redirectURL = config.redirectURL || '/app';
|
||||||
let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
|
// let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
|
||||||
|
// Note: If OIDC breaks in the future, use the above params
|
||||||
|
let params = `state=${globalState.state}`;
|
||||||
|
|
||||||
if (code !== '') {
|
if (code !== '') {
|
||||||
params += `&code=${code}`;
|
params += `&code=${code}`;
|
||||||
|
@@ -32,29 +32,35 @@ const FooterContent = styled.div`
|
|||||||
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
||||||
const { config } = useAuthorizer();
|
const { config } = useAuthorizer();
|
||||||
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
|
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
|
||||||
|
const isBasicAuth = config.is_basic_authentication_enabled;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{view === VIEW_TYPES.LOGIN && (
|
{view === VIEW_TYPES.LOGIN && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<h1 style={{ textAlign: 'center' }}>Login</h1>
|
<h1 style={{ textAlign: 'center' }}>Login</h1>
|
||||||
<br />
|
|
||||||
<AuthorizerSocialLogin urlProps={urlProps} />
|
<AuthorizerSocialLogin urlProps={urlProps} />
|
||||||
{config.is_basic_authentication_enabled &&
|
<br />
|
||||||
|
{(config.is_basic_authentication_enabled ||
|
||||||
|
config.is_mobile_basic_authentication_enabled) &&
|
||||||
!config.is_magic_link_login_enabled && (
|
!config.is_magic_link_login_enabled && (
|
||||||
<AuthorizerBasicAuthLogin urlProps={urlProps} />
|
<AuthorizerBasicAuthLogin urlProps={urlProps} />
|
||||||
)}
|
)}
|
||||||
{config.is_magic_link_login_enabled && (
|
{config.is_magic_link_login_enabled && (
|
||||||
<AuthorizerMagicLinkLogin urlProps={urlProps} />
|
<AuthorizerMagicLinkLogin urlProps={urlProps} />
|
||||||
)}
|
)}
|
||||||
<Footer>
|
{(config.is_basic_authentication_enabled ||
|
||||||
<Link
|
config.is_mobile_basic_authentication_enabled) &&
|
||||||
to="#"
|
!config.is_magic_link_login_enabled && (
|
||||||
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
|
<Footer>
|
||||||
style={{ marginBottom: 10 }}
|
<Link
|
||||||
>
|
to="#"
|
||||||
Forgot Password?
|
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
|
||||||
</Link>
|
style={{ marginBottom: 10 }}
|
||||||
</Footer>
|
>
|
||||||
|
Forgot Password?
|
||||||
|
</Link>
|
||||||
|
</Footer>
|
||||||
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
{view === VIEW_TYPES.FORGOT_PASSWORD && (
|
{view === VIEW_TYPES.FORGOT_PASSWORD && (
|
||||||
@@ -65,6 +71,9 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
|||||||
...urlProps,
|
...urlProps,
|
||||||
redirect_uri: `${window.location.origin}/app/reset-password`,
|
redirect_uri: `${window.location.origin}/app/reset-password`,
|
||||||
}}
|
}}
|
||||||
|
onPasswordReset={() => {
|
||||||
|
setView(VIEW_TYPES.LOGIN);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Footer>
|
<Footer>
|
||||||
<Link
|
<Link
|
||||||
@@ -81,7 +90,7 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
|||||||
!config.is_magic_link_login_enabled &&
|
!config.is_magic_link_login_enabled &&
|
||||||
config.is_sign_up_enabled && (
|
config.is_sign_up_enabled && (
|
||||||
<FooterContent>
|
<FooterContent>
|
||||||
Don't have an account? <Link to="/app/signup"> Sign Up</Link>
|
Don't have an account? <Link to="/app/signup"> Sign Up</Link>
|
||||||
</FooterContent>
|
</FooterContent>
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { AuthorizerSignup } from '@authorizerdev/authorizer-react';
|
import { AuthorizerSignup, AuthorizerSocialLogin } from '@authorizerdev/authorizer-react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ export default function SignUp({
|
|||||||
<Fragment>
|
<Fragment>
|
||||||
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
|
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
|
||||||
<br />
|
<br />
|
||||||
|
<AuthorizerSocialLogin urlProps={urlProps} />
|
||||||
<AuthorizerSignup urlProps={urlProps} />
|
<AuthorizerSignup urlProps={urlProps} />
|
||||||
<FooterContent>
|
<FooterContent>
|
||||||
Already have an account? <Link to="/app"> Login</Link>
|
Already have an account? <Link to="/app"> Login</Link>
|
||||||
|
227
app/yarn.lock
227
app/yarn.lock
@@ -2,35 +2,38 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@authorizerdev/authorizer-js@^1.2.6":
|
"@authorizerdev/authorizer-js@^2.0.0-beta.3":
|
||||||
"integrity" "sha512-9+9phHUMF+AeDM0y+XQvIRDoerOXnQ1vfTfYN6KxWN1apdrkAd9nzS1zUsA2uJSnX3fFZOErn83GjbYYCYF1BA=="
|
version "2.0.0-beta.3"
|
||||||
"resolved" "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.2.6.tgz"
|
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-2.0.0-beta.3.tgz"
|
||||||
"version" "1.2.6"
|
integrity sha512-cEzEVe7AewvOwOwoettiKRCq1e5Y33k9g8fJjqAoe3B/36iNN8wnZ5qgsPPZkqhv+Cvn6huj+YWtRimfVJ6d0w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"cross-fetch" "^3.1.5"
|
"cross-fetch" "^3.1.5"
|
||||||
|
|
||||||
"@authorizerdev/authorizer-react@^1.1.13":
|
"@authorizerdev/authorizer-react@^1.2.0":
|
||||||
"integrity" "sha512-LmpzyfR0+nEn+bjUrb/QU9b3kiVoYzMBIvcQ1nV4TNvrvVSqbLPKk+GmoIPkiBEtfy/QSM6XFLkiGNGD9BRP+g=="
|
version "1.2.0"
|
||||||
"resolved" "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.13.tgz"
|
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.2.0.tgz"
|
||||||
"version" "1.1.13"
|
integrity sha512-MtunZgh30rzY9jSADVP1DRC4sOBC82zx/yhK8O/1ufOAi7vTDZwPjDHIMrG/xWPNUYTCeFPEKpZlKyB+TH/M1w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@authorizerdev/authorizer-js" "^1.2.6"
|
"@authorizerdev/authorizer-js" "^2.0.0-beta.3"
|
||||||
|
validator "^13.11.0"
|
||||||
|
|
||||||
"@babel/code-frame@^7.16.7":
|
"@babel/code-frame@^7.22.13":
|
||||||
"integrity" "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg=="
|
"integrity" "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz"
|
||||||
"version" "7.16.7"
|
"version" "7.22.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/highlight" "^7.16.7"
|
"@babel/highlight" "^7.22.13"
|
||||||
|
"chalk" "^2.4.2"
|
||||||
|
|
||||||
"@babel/generator@^7.16.8":
|
"@babel/generator@^7.23.0":
|
||||||
"integrity" "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw=="
|
"integrity" "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz"
|
||||||
"version" "7.16.8"
|
"version" "7.23.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.16.8"
|
"@babel/types" "^7.23.0"
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.2"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.17"
|
||||||
"jsesc" "^2.5.1"
|
"jsesc" "^2.5.1"
|
||||||
"source-map" "^0.5.0"
|
|
||||||
|
|
||||||
"@babel/helper-annotate-as-pure@^7.16.0":
|
"@babel/helper-annotate-as-pure@^7.16.0":
|
||||||
"integrity" "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw=="
|
"integrity" "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw=="
|
||||||
@@ -39,35 +42,25 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.16.7"
|
||||||
|
|
||||||
"@babel/helper-environment-visitor@^7.16.7":
|
"@babel/helper-environment-visitor@^7.22.20":
|
||||||
"integrity" "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag=="
|
"integrity" "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
|
||||||
"version" "7.16.7"
|
"version" "7.22.20"
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.16.7"
|
|
||||||
|
|
||||||
"@babel/helper-function-name@^7.16.7":
|
"@babel/helper-function-name@^7.23.0":
|
||||||
"integrity" "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA=="
|
"integrity" "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
|
||||||
"version" "7.16.7"
|
"version" "7.23.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-get-function-arity" "^7.16.7"
|
"@babel/template" "^7.22.15"
|
||||||
"@babel/template" "^7.16.7"
|
"@babel/types" "^7.23.0"
|
||||||
"@babel/types" "^7.16.7"
|
|
||||||
|
|
||||||
"@babel/helper-get-function-arity@^7.16.7":
|
"@babel/helper-hoist-variables@^7.22.5":
|
||||||
"integrity" "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw=="
|
"integrity" "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
|
||||||
"version" "7.16.7"
|
"version" "7.22.5"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/helper-hoist-variables@^7.16.7":
|
|
||||||
"integrity" "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg=="
|
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz"
|
|
||||||
"version" "7.16.7"
|
|
||||||
dependencies:
|
|
||||||
"@babel/types" "^7.16.7"
|
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0":
|
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0":
|
||||||
"integrity" "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg=="
|
"integrity" "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg=="
|
||||||
@@ -76,31 +69,36 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.16.7"
|
||||||
|
|
||||||
"@babel/helper-split-export-declaration@^7.16.7":
|
"@babel/helper-split-export-declaration@^7.22.6":
|
||||||
"integrity" "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw=="
|
"integrity" "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
|
||||||
"version" "7.16.7"
|
"version" "7.22.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/helper-validator-identifier@^7.16.7":
|
"@babel/helper-string-parser@^7.22.5":
|
||||||
"integrity" "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
|
"integrity" "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz"
|
||||||
"version" "7.16.7"
|
"version" "7.22.5"
|
||||||
|
|
||||||
"@babel/highlight@^7.16.7":
|
"@babel/helper-validator-identifier@^7.22.20":
|
||||||
"integrity" "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw=="
|
"integrity" "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
|
||||||
"version" "7.16.10"
|
"version" "7.22.20"
|
||||||
|
|
||||||
|
"@babel/highlight@^7.22.13":
|
||||||
|
"integrity" "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg=="
|
||||||
|
"resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz"
|
||||||
|
"version" "7.22.20"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-validator-identifier" "^7.16.7"
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
"chalk" "^2.0.0"
|
"chalk" "^2.4.2"
|
||||||
"js-tokens" "^4.0.0"
|
"js-tokens" "^4.0.0"
|
||||||
|
|
||||||
"@babel/parser@^7.16.10", "@babel/parser@^7.16.7":
|
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
|
||||||
"integrity" "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A=="
|
"integrity" "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz"
|
||||||
"version" "7.16.12"
|
"version" "7.23.0"
|
||||||
|
|
||||||
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1":
|
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1":
|
||||||
"integrity" "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg=="
|
"integrity" "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg=="
|
||||||
@@ -109,37 +107,38 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"regenerator-runtime" "^0.13.4"
|
"regenerator-runtime" "^0.13.4"
|
||||||
|
|
||||||
"@babel/template@^7.16.7":
|
"@babel/template@^7.22.15":
|
||||||
"integrity" "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w=="
|
"integrity" "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
|
||||||
"version" "7.16.7"
|
"version" "7.22.15"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.16.7"
|
"@babel/code-frame" "^7.22.13"
|
||||||
"@babel/parser" "^7.16.7"
|
"@babel/parser" "^7.22.15"
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.22.15"
|
||||||
|
|
||||||
"@babel/traverse@^7.4.5":
|
"@babel/traverse@^7.4.5":
|
||||||
"integrity" "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw=="
|
"integrity" "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz"
|
||||||
"version" "7.16.10"
|
"version" "7.23.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.16.7"
|
"@babel/code-frame" "^7.22.13"
|
||||||
"@babel/generator" "^7.16.8"
|
"@babel/generator" "^7.23.0"
|
||||||
"@babel/helper-environment-visitor" "^7.16.7"
|
"@babel/helper-environment-visitor" "^7.22.20"
|
||||||
"@babel/helper-function-name" "^7.16.7"
|
"@babel/helper-function-name" "^7.23.0"
|
||||||
"@babel/helper-hoist-variables" "^7.16.7"
|
"@babel/helper-hoist-variables" "^7.22.5"
|
||||||
"@babel/helper-split-export-declaration" "^7.16.7"
|
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||||
"@babel/parser" "^7.16.10"
|
"@babel/parser" "^7.23.0"
|
||||||
"@babel/types" "^7.16.8"
|
"@babel/types" "^7.23.0"
|
||||||
"debug" "^4.1.0"
|
"debug" "^4.1.0"
|
||||||
"globals" "^11.1.0"
|
"globals" "^11.1.0"
|
||||||
|
|
||||||
"@babel/types@^7.16.7", "@babel/types@^7.16.8":
|
"@babel/types@^7.16.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
|
||||||
"integrity" "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg=="
|
"integrity" "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz"
|
||||||
"version" "7.16.8"
|
"version" "7.23.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-validator-identifier" "^7.16.7"
|
"@babel/helper-string-parser" "^7.22.5"
|
||||||
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
"to-fast-properties" "^2.0.0"
|
"to-fast-properties" "^2.0.0"
|
||||||
|
|
||||||
"@emotion/is-prop-valid@^0.8.8":
|
"@emotion/is-prop-valid@^0.8.8":
|
||||||
@@ -164,6 +163,38 @@
|
|||||||
"resolved" "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
|
"resolved" "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
|
||||||
"version" "0.7.5"
|
"version" "0.7.5"
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping@^0.3.2":
|
||||||
|
"integrity" "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ=="
|
||||||
|
"resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz"
|
||||||
|
"version" "0.3.3"
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/set-array" "^1.0.1"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri@^3.1.0":
|
||||||
|
"integrity" "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA=="
|
||||||
|
"resolved" "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz"
|
||||||
|
"version" "3.1.1"
|
||||||
|
|
||||||
|
"@jridgewell/set-array@^1.0.1":
|
||||||
|
"integrity" "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
|
||||||
|
"resolved" "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
|
||||||
|
"version" "1.1.2"
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
|
||||||
|
"integrity" "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||||
|
"resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
||||||
|
"version" "1.4.15"
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
|
||||||
|
"integrity" "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q=="
|
||||||
|
"resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz"
|
||||||
|
"version" "0.3.20"
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri" "^3.1.0"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||||
|
|
||||||
"@types/history@*":
|
"@types/history@*":
|
||||||
"integrity" "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ=="
|
"integrity" "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ=="
|
||||||
"resolved" "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz"
|
"resolved" "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz"
|
||||||
@@ -256,7 +287,7 @@
|
|||||||
"resolved" "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
|
"resolved" "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
|
||||||
"version" "1.0.0"
|
"version" "1.0.0"
|
||||||
|
|
||||||
"chalk@^2.0.0":
|
"chalk@^2.4.2":
|
||||||
"integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="
|
"integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="
|
||||||
"resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
|
"resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
|
||||||
"version" "2.4.2"
|
"version" "2.4.2"
|
||||||
@@ -273,7 +304,7 @@
|
|||||||
"color-name" "1.1.3"
|
"color-name" "1.1.3"
|
||||||
|
|
||||||
"color-name@1.1.3":
|
"color-name@1.1.3":
|
||||||
"integrity" "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
"integrity" "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||||
"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"
|
||||||
"version" "1.1.3"
|
"version" "1.1.3"
|
||||||
|
|
||||||
@@ -316,7 +347,7 @@
|
|||||||
"version" "0.12.17"
|
"version" "0.12.17"
|
||||||
|
|
||||||
"escape-string-regexp@^1.0.5":
|
"escape-string-regexp@^1.0.5":
|
||||||
"integrity" "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
"integrity" "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
|
||||||
"resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
|
"resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
|
||||||
"version" "1.0.5"
|
"version" "1.0.5"
|
||||||
|
|
||||||
@@ -390,9 +421,9 @@
|
|||||||
"version" "2.1.2"
|
"version" "2.1.2"
|
||||||
|
|
||||||
"node-fetch@^2.6.12":
|
"node-fetch@^2.6.12":
|
||||||
"integrity" "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g=="
|
"integrity" "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="
|
||||||
"resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz"
|
"resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
||||||
"version" "2.6.12"
|
"version" "2.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"whatwg-url" "^5.0.0"
|
"whatwg-url" "^5.0.0"
|
||||||
|
|
||||||
@@ -516,11 +547,6 @@
|
|||||||
"resolved" "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
|
"resolved" "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
|
||||||
"version" "1.1.0"
|
"version" "1.1.0"
|
||||||
|
|
||||||
"source-map@^0.5.0":
|
|
||||||
"integrity" "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
|
||||||
"resolved" "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz"
|
|
||||||
"version" "0.5.7"
|
|
||||||
|
|
||||||
"styled-components@^5.3.0", "styled-components@>= 2":
|
"styled-components@^5.3.0", "styled-components@>= 2":
|
||||||
"integrity" "sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw=="
|
"integrity" "sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw=="
|
||||||
"resolved" "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz"
|
"resolved" "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz"
|
||||||
@@ -569,6 +595,11 @@
|
|||||||
"resolved" "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz"
|
"resolved" "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz"
|
||||||
"version" "4.3.5"
|
"version" "4.3.5"
|
||||||
|
|
||||||
|
"validator@^13.11.0":
|
||||||
|
"integrity" "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ=="
|
||||||
|
"resolved" "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz"
|
||||||
|
"version" "13.11.0"
|
||||||
|
|
||||||
"value-equal@^1.0.1":
|
"value-equal@^1.0.1":
|
||||||
"integrity" "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
"integrity" "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
||||||
"resolved" "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz"
|
"resolved" "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz"
|
||||||
|
2590
dashboard/package-lock.json
generated
2590
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@ const Features = ({ variables, setVariables }: any) => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex>
|
<Flex>
|
||||||
<Flex w="100%" justifyContent="start" alignItems="center">
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
<Text fontSize="sm">Email Verification:</Text>
|
<Text fontSize="sm">Email Verification:</Text>
|
||||||
@@ -97,6 +98,7 @@ const Features = ({ variables, setVariables }: any) => {
|
|||||||
also ignore the user MFA setting.
|
also ignore the user MFA setting.
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex justifyContent="start" mb={3}>
|
<Flex justifyContent="start" mb={3}>
|
||||||
<InputField
|
<InputField
|
||||||
variables={variables}
|
variables={variables}
|
||||||
@@ -106,6 +108,41 @@ const Features = ({ variables, setVariables }: any) => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && (
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Flex w="100%" alignItems="baseline" flexDir="column">
|
||||||
|
<Text fontSize="sm">Time Based OTP (TOTP):</Text>
|
||||||
|
<Text fontSize="x-small">Note: to enable totp mfa</Text>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex justifyContent="start" mb={3}>
|
||||||
|
<InputField
|
||||||
|
variables={variables}
|
||||||
|
setVariables={setVariables}
|
||||||
|
inputType={SwitchInputType.DISABLE_TOTP_LOGIN}
|
||||||
|
hasReversedValue
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && (
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Flex w="100%" alignItems="baseline" flexDir="column">
|
||||||
|
<Text fontSize="sm">EMAIL OTP:</Text>
|
||||||
|
<Text fontSize="x-small">Note: to enable email otp mfa</Text>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Flex justifyContent="start" mb={3}>
|
||||||
|
<InputField
|
||||||
|
variables={variables}
|
||||||
|
setVariables={setVariables}
|
||||||
|
inputType={SwitchInputType.DISABLE_MAIL_OTP_LOGIN}
|
||||||
|
hasReversedValue
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<Flex w="100%" alignItems="baseline" flexDir="column">
|
<Flex w="100%" alignItems="baseline" flexDir="column">
|
||||||
<Text fontSize="sm">
|
<Text fontSize="sm">
|
||||||
@@ -124,6 +161,19 @@ const Features = ({ variables, setVariables }: any) => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Flex w="100%" justifyContent="start" alignItems="center">
|
||||||
|
<Text fontSize="sm">Playground:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Flex justifyContent="start">
|
||||||
|
<InputField
|
||||||
|
variables={variables}
|
||||||
|
setVariables={setVariables}
|
||||||
|
inputType={SwitchInputType.DISABLE_PLAYGROUND}
|
||||||
|
hasReversedValue
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Divider paddingY={5} />
|
<Divider paddingY={5} />
|
||||||
<Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
|
<Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
|
||||||
|
@@ -17,6 +17,7 @@ import {
|
|||||||
FaApple,
|
FaApple,
|
||||||
FaTwitter,
|
FaTwitter,
|
||||||
FaMicrosoft,
|
FaMicrosoft,
|
||||||
|
FaTwitch, FaDiscord,
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import {
|
import {
|
||||||
TextInputType,
|
TextInputType,
|
||||||
@@ -308,6 +309,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"
|
||||||
|
>
|
||||||
|
<FaDiscord style={{ color: '#7289da' }} />
|
||||||
|
</Center>
|
||||||
|
<Center
|
||||||
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
|
mt={isNotSmallerScreen ? '0' : '3'}
|
||||||
|
marginRight="1.5%"
|
||||||
|
>
|
||||||
|
<InputField
|
||||||
|
borderRadius={5}
|
||||||
|
variables={envVariables}
|
||||||
|
setVariables={setVariables}
|
||||||
|
inputType={TextInputType.DISCORD_CLIENT_ID}
|
||||||
|
placeholder="Discord Client ID"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
<Center
|
||||||
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
|
mt={isNotSmallerScreen ? '0' : '3'}
|
||||||
|
>
|
||||||
|
<InputField
|
||||||
|
borderRadius={5}
|
||||||
|
variables={envVariables}
|
||||||
|
setVariables={setVariables}
|
||||||
|
fieldVisibility={fieldVisibility}
|
||||||
|
setFieldVisibility={setFieldVisibility}
|
||||||
|
inputType={HiddenInputType.DISCORD_CLIENT_SECRET}
|
||||||
|
placeholder="Discord Client Secret"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||||
<Center
|
<Center
|
||||||
w={isNotSmallerScreen ? '55px' : '35px'}
|
w={isNotSmallerScreen ? '55px' : '35px'}
|
||||||
@@ -397,6 +436,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"
|
||||||
|
>
|
||||||
|
<FaTwitch />
|
||||||
|
</Center>
|
||||||
|
<Center
|
||||||
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
|
mt={isNotSmallerScreen ? '0' : '3'}
|
||||||
|
marginRight="1.5%"
|
||||||
|
>
|
||||||
|
<InputField
|
||||||
|
borderRadius={5}
|
||||||
|
variables={envVariables}
|
||||||
|
setVariables={setVariables}
|
||||||
|
inputType={TextInputType.TWITCH_CLIENT_ID}
|
||||||
|
placeholder="Twitch Client ID"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
<Center
|
||||||
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
|
mt={isNotSmallerScreen ? '0' : '3'}
|
||||||
|
>
|
||||||
|
<InputField
|
||||||
|
borderRadius={5}
|
||||||
|
variables={envVariables}
|
||||||
|
setVariables={setVariables}
|
||||||
|
fieldVisibility={fieldVisibility}
|
||||||
|
setFieldVisibility={setFieldVisibility}
|
||||||
|
inputType={HiddenInputType.TWITCH_CLIENT_SECRET}
|
||||||
|
placeholder="Twitch Client Secret"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -9,9 +9,11 @@ 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',
|
||||||
|
DISCORD_CLIENT_ID: 'DISCORD_CLIENT_ID',
|
||||||
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
|
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
|
||||||
MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID',
|
MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID',
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID',
|
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID',
|
||||||
|
TWITCH_CLIENT_ID: 'TWITCH_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',
|
||||||
@@ -40,8 +42,10 @@ 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',
|
||||||
|
DISCORD_CLIENT_SECRET: 'DISCORD_CLIENT_SECRET',
|
||||||
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
|
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
|
||||||
MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET',
|
MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET',
|
||||||
|
TWITCH_CLIENT_SECRET: 'TWITCH_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',
|
||||||
@@ -84,6 +88,9 @@ export const SwitchInputType = {
|
|||||||
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
|
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
|
||||||
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
|
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
|
||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
|
||||||
|
DISABLE_PLAYGROUND: 'DISABLE_PLAYGROUND',
|
||||||
|
DISABLE_TOTP_LOGIN: 'DISABLE_TOTP_LOGIN',
|
||||||
|
DISABLE_MAIL_OTP_LOGIN: 'DISABLE_MAIL_OTP_LOGIN',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DateInputType = {
|
export const DateInputType = {
|
||||||
@@ -124,11 +131,15 @@ 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;
|
||||||
|
DISCORD_CLIENT_ID: string;
|
||||||
|
DISCORD_CLIENT_SECRET: string;
|
||||||
TWITTER_CLIENT_ID: string;
|
TWITTER_CLIENT_ID: string;
|
||||||
TWITTER_CLIENT_SECRET: string;
|
TWITTER_CLIENT_SECRET: string;
|
||||||
MICROSOFT_CLIENT_ID: string;
|
MICROSOFT_CLIENT_ID: string;
|
||||||
MICROSOFT_CLIENT_SECRET: string;
|
MICROSOFT_CLIENT_SECRET: string;
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: string;
|
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: string;
|
||||||
|
TWITCH_CLIENT_ID: string;
|
||||||
|
TWITCH_CLIENT_SECRET: string;
|
||||||
ROLES: [string] | [];
|
ROLES: [string] | [];
|
||||||
DEFAULT_ROLES: [string] | [];
|
DEFAULT_ROLES: [string] | [];
|
||||||
PROTECTED_ROLES: [string] | [];
|
PROTECTED_ROLES: [string] | [];
|
||||||
@@ -167,6 +178,9 @@ export interface envVarTypes {
|
|||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE: string;
|
DEFAULT_AUTHORIZE_RESPONSE_TYPE: string;
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE: string;
|
DEFAULT_AUTHORIZE_RESPONSE_MODE: string;
|
||||||
|
DISABLE_PLAYGROUND: boolean;
|
||||||
|
DISABLE_TOTP_LOGIN: boolean;
|
||||||
|
DISABLE_MAIL_OTP_LOGIN: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const envSubViews = {
|
export const envSubViews = {
|
||||||
@@ -220,6 +234,7 @@ export const webhookEventNames = {
|
|||||||
'User deleted': 'user.deleted',
|
'User deleted': 'user.deleted',
|
||||||
'User access enabled': 'user.access_enabled',
|
'User access enabled': 'user.access_enabled',
|
||||||
'User access revoked': 'user.access_revoked',
|
'User access revoked': 'user.access_revoked',
|
||||||
|
'User deactivated': 'user.deactivated',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const emailTemplateEventNames = {
|
export const emailTemplateEventNames = {
|
||||||
|
@@ -30,11 +30,15 @@ export const EnvVariablesQuery = `
|
|||||||
LINKEDIN_CLIENT_SECRET
|
LINKEDIN_CLIENT_SECRET
|
||||||
APPLE_CLIENT_ID
|
APPLE_CLIENT_ID
|
||||||
APPLE_CLIENT_SECRET
|
APPLE_CLIENT_SECRET
|
||||||
|
DISCORD_CLIENT_ID
|
||||||
|
DISCORD_CLIENT_SECRET
|
||||||
TWITTER_CLIENT_ID
|
TWITTER_CLIENT_ID
|
||||||
TWITTER_CLIENT_SECRET
|
TWITTER_CLIENT_SECRET
|
||||||
MICROSOFT_CLIENT_ID
|
MICROSOFT_CLIENT_ID
|
||||||
MICROSOFT_CLIENT_SECRET
|
MICROSOFT_CLIENT_SECRET
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
|
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
|
||||||
|
TWITCH_CLIENT_ID
|
||||||
|
TWITCH_CLIENT_SECRET
|
||||||
DEFAULT_ROLES
|
DEFAULT_ROLES
|
||||||
PROTECTED_ROLES
|
PROTECTED_ROLES
|
||||||
ROLES
|
ROLES
|
||||||
@@ -73,6 +77,9 @@ export const EnvVariablesQuery = `
|
|||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE
|
DEFAULT_AUTHORIZE_RESPONSE_TYPE
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE
|
DEFAULT_AUTHORIZE_RESPONSE_MODE
|
||||||
|
DISABLE_PLAYGROUND
|
||||||
|
DISABLE_TOTP_LOGIN
|
||||||
|
DISABLE_MAIL_OTP_LOGIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -50,11 +50,15 @@ const Environment = () => {
|
|||||||
LINKEDIN_CLIENT_SECRET: '',
|
LINKEDIN_CLIENT_SECRET: '',
|
||||||
APPLE_CLIENT_ID: '',
|
APPLE_CLIENT_ID: '',
|
||||||
APPLE_CLIENT_SECRET: '',
|
APPLE_CLIENT_SECRET: '',
|
||||||
|
DISCORD_CLIENT_ID: '',
|
||||||
|
DISCORD_CLIENT_SECRET: '',
|
||||||
TWITTER_CLIENT_ID: '',
|
TWITTER_CLIENT_ID: '',
|
||||||
TWITTER_CLIENT_SECRET: '',
|
TWITTER_CLIENT_SECRET: '',
|
||||||
MICROSOFT_CLIENT_ID: '',
|
MICROSOFT_CLIENT_ID: '',
|
||||||
MICROSOFT_CLIENT_SECRET: '',
|
MICROSOFT_CLIENT_SECRET: '',
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: '',
|
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: '',
|
||||||
|
TWITCH_CLIENT_ID: '',
|
||||||
|
TWITCH_CLIENT_SECRET: '',
|
||||||
ROLES: [],
|
ROLES: [],
|
||||||
DEFAULT_ROLES: [],
|
DEFAULT_ROLES: [],
|
||||||
PROTECTED_ROLES: [],
|
PROTECTED_ROLES: [],
|
||||||
@@ -93,6 +97,9 @@ const Environment = () => {
|
|||||||
ENFORCE_MULTI_FACTOR_AUTHENTICATION: false,
|
ENFORCE_MULTI_FACTOR_AUTHENTICATION: false,
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE: '',
|
DEFAULT_AUTHORIZE_RESPONSE_TYPE: '',
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE: '',
|
DEFAULT_AUTHORIZE_RESPONSE_MODE: '',
|
||||||
|
DISABLE_PLAYGROUND: false,
|
||||||
|
DISABLE_TOTP_LOGIN: false,
|
||||||
|
DISABLE_MAIL_OTP_LOGIN: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [fieldVisibility, setFieldVisibility] = React.useState<
|
const [fieldVisibility, setFieldVisibility] = React.useState<
|
||||||
@@ -103,7 +110,9 @@ 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,
|
||||||
|
DISCORD_CLIENT_SECRET: false,
|
||||||
TWITTER_CLIENT_SECRET: false,
|
TWITTER_CLIENT_SECRET: false,
|
||||||
|
TWITCH_CLIENT_SECRET: false,
|
||||||
JWT_SECRET: false,
|
JWT_SECRET: false,
|
||||||
SMTP_PASSWORD: false,
|
SMTP_PASSWORD: false,
|
||||||
ADMIN_SECRET: false,
|
ADMIN_SECRET: false,
|
||||||
|
@@ -118,7 +118,6 @@ const Webhooks = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchWebookData();
|
fetchWebookData();
|
||||||
}, [paginationProps.page, paginationProps.limit]);
|
}, [paginationProps.page, paginationProps.limit]);
|
||||||
console.log({ webhookData });
|
|
||||||
return (
|
return (
|
||||||
<Box m="5" py="5" px="10" bg="white" rounded="md">
|
<Box m="5" py="5" px="10" bg="white" rounded="md">
|
||||||
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
|
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
|
||||||
|
@@ -10,157 +10,155 @@
|
|||||||
"@jridgewell/gen-mapping" "^0.3.0"
|
"@jridgewell/gen-mapping" "^0.3.0"
|
||||||
"@jridgewell/trace-mapping" "^0.3.9"
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4":
|
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13":
|
||||||
"integrity" "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g=="
|
"integrity" "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz"
|
||||||
"version" "7.21.4"
|
"version" "7.22.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/highlight" "^7.18.6"
|
"@babel/highlight" "^7.22.13"
|
||||||
|
"chalk" "^2.4.2"
|
||||||
|
|
||||||
"@babel/compat-data@^7.21.4":
|
"@babel/compat-data@^7.22.9":
|
||||||
"integrity" "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g=="
|
"integrity" "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz"
|
||||||
"version" "7.21.4"
|
"version" "7.23.3"
|
||||||
|
|
||||||
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0":
|
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0":
|
||||||
"integrity" "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA=="
|
"integrity" "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz"
|
||||||
"version" "7.21.4"
|
"version" "7.23.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ampproject/remapping" "^2.2.0"
|
"@ampproject/remapping" "^2.2.0"
|
||||||
"@babel/code-frame" "^7.21.4"
|
"@babel/code-frame" "^7.22.13"
|
||||||
"@babel/generator" "^7.21.4"
|
"@babel/generator" "^7.23.3"
|
||||||
"@babel/helper-compilation-targets" "^7.21.4"
|
"@babel/helper-compilation-targets" "^7.22.15"
|
||||||
"@babel/helper-module-transforms" "^7.21.2"
|
"@babel/helper-module-transforms" "^7.23.3"
|
||||||
"@babel/helpers" "^7.21.0"
|
"@babel/helpers" "^7.23.2"
|
||||||
"@babel/parser" "^7.21.4"
|
"@babel/parser" "^7.23.3"
|
||||||
"@babel/template" "^7.20.7"
|
"@babel/template" "^7.22.15"
|
||||||
"@babel/traverse" "^7.21.4"
|
"@babel/traverse" "^7.23.3"
|
||||||
"@babel/types" "^7.21.4"
|
"@babel/types" "^7.23.3"
|
||||||
"convert-source-map" "^1.7.0"
|
"convert-source-map" "^2.0.0"
|
||||||
"debug" "^4.1.0"
|
"debug" "^4.1.0"
|
||||||
"gensync" "^1.0.0-beta.2"
|
"gensync" "^1.0.0-beta.2"
|
||||||
"json5" "^2.2.2"
|
"json5" "^2.2.3"
|
||||||
"semver" "^6.3.0"
|
"semver" "^6.3.1"
|
||||||
|
|
||||||
"@babel/generator@^7.21.4":
|
"@babel/generator@^7.23.3":
|
||||||
"integrity" "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA=="
|
"integrity" "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz"
|
||||||
"version" "7.21.4"
|
"version" "7.23.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.21.4"
|
"@babel/types" "^7.23.3"
|
||||||
"@jridgewell/gen-mapping" "^0.3.2"
|
"@jridgewell/gen-mapping" "^0.3.2"
|
||||||
"@jridgewell/trace-mapping" "^0.3.17"
|
"@jridgewell/trace-mapping" "^0.3.17"
|
||||||
"jsesc" "^2.5.1"
|
"jsesc" "^2.5.1"
|
||||||
|
|
||||||
"@babel/helper-compilation-targets@^7.21.4":
|
"@babel/helper-compilation-targets@^7.22.15":
|
||||||
"integrity" "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg=="
|
"integrity" "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz"
|
||||||
"version" "7.21.4"
|
"version" "7.22.15"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/compat-data" "^7.21.4"
|
"@babel/compat-data" "^7.22.9"
|
||||||
"@babel/helper-validator-option" "^7.21.0"
|
"@babel/helper-validator-option" "^7.22.15"
|
||||||
"browserslist" "^4.21.3"
|
"browserslist" "^4.21.9"
|
||||||
"lru-cache" "^5.1.1"
|
"lru-cache" "^5.1.1"
|
||||||
"semver" "^6.3.0"
|
"semver" "^6.3.1"
|
||||||
|
|
||||||
"@babel/helper-environment-visitor@^7.18.9":
|
"@babel/helper-environment-visitor@^7.22.20":
|
||||||
"integrity" "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg=="
|
"integrity" "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
|
||||||
"version" "7.18.9"
|
"version" "7.22.20"
|
||||||
|
|
||||||
"@babel/helper-function-name@^7.21.0":
|
"@babel/helper-function-name@^7.23.0":
|
||||||
"integrity" "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg=="
|
"integrity" "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
|
||||||
"version" "7.21.0"
|
"version" "7.23.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/template" "^7.20.7"
|
"@babel/template" "^7.22.15"
|
||||||
"@babel/types" "^7.21.0"
|
"@babel/types" "^7.23.0"
|
||||||
|
|
||||||
"@babel/helper-hoist-variables@^7.18.6":
|
"@babel/helper-hoist-variables@^7.22.5":
|
||||||
"integrity" "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q=="
|
"integrity" "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
|
||||||
"version" "7.18.6"
|
"version" "7.22.5"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.18.6"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6":
|
"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.22.15":
|
||||||
"integrity" "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="
|
"integrity" "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz"
|
||||||
"version" "7.18.6"
|
"version" "7.22.15"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.18.6"
|
"@babel/types" "^7.22.15"
|
||||||
|
|
||||||
"@babel/helper-module-transforms@^7.21.2":
|
"@babel/helper-module-transforms@^7.23.3":
|
||||||
"integrity" "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ=="
|
"integrity" "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz"
|
||||||
"version" "7.21.2"
|
"version" "7.23.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-environment-visitor" "^7.18.9"
|
"@babel/helper-environment-visitor" "^7.22.20"
|
||||||
"@babel/helper-module-imports" "^7.18.6"
|
"@babel/helper-module-imports" "^7.22.15"
|
||||||
"@babel/helper-simple-access" "^7.20.2"
|
"@babel/helper-simple-access" "^7.22.5"
|
||||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||||
"@babel/helper-validator-identifier" "^7.19.1"
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
"@babel/template" "^7.20.7"
|
|
||||||
"@babel/traverse" "^7.21.2"
|
|
||||||
"@babel/types" "^7.21.2"
|
|
||||||
|
|
||||||
"@babel/helper-plugin-utils@^7.16.5":
|
"@babel/helper-plugin-utils@^7.16.5":
|
||||||
"integrity" "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ=="
|
"integrity" "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz"
|
||||||
"version" "7.16.5"
|
"version" "7.16.5"
|
||||||
|
|
||||||
"@babel/helper-simple-access@^7.20.2":
|
"@babel/helper-simple-access@^7.22.5":
|
||||||
"integrity" "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA=="
|
"integrity" "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz"
|
||||||
"version" "7.20.2"
|
"version" "7.22.5"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.20.2"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/helper-split-export-declaration@^7.18.6":
|
"@babel/helper-split-export-declaration@^7.22.6":
|
||||||
"integrity" "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA=="
|
"integrity" "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
|
||||||
"version" "7.18.6"
|
"version" "7.22.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.18.6"
|
"@babel/types" "^7.22.5"
|
||||||
|
|
||||||
"@babel/helper-string-parser@^7.19.4":
|
"@babel/helper-string-parser@^7.22.5":
|
||||||
"integrity" "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw=="
|
"integrity" "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz"
|
||||||
"version" "7.19.4"
|
"version" "7.22.5"
|
||||||
|
|
||||||
"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
|
"@babel/helper-validator-identifier@^7.22.20":
|
||||||
"integrity" "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w=="
|
"integrity" "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
|
||||||
"version" "7.19.1"
|
"version" "7.22.20"
|
||||||
|
|
||||||
"@babel/helper-validator-option@^7.21.0":
|
"@babel/helper-validator-option@^7.22.15":
|
||||||
"integrity" "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ=="
|
"integrity" "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz"
|
||||||
"version" "7.21.0"
|
"version" "7.22.15"
|
||||||
|
|
||||||
"@babel/helpers@^7.21.0":
|
"@babel/helpers@^7.23.2":
|
||||||
"integrity" "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA=="
|
"integrity" "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz"
|
||||||
"version" "7.21.0"
|
"version" "7.23.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/template" "^7.20.7"
|
"@babel/template" "^7.22.15"
|
||||||
"@babel/traverse" "^7.21.0"
|
"@babel/traverse" "^7.23.2"
|
||||||
"@babel/types" "^7.21.0"
|
"@babel/types" "^7.23.0"
|
||||||
|
|
||||||
"@babel/highlight@^7.18.6":
|
"@babel/highlight@^7.22.13":
|
||||||
"integrity" "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g=="
|
"integrity" "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz"
|
||||||
"version" "7.18.6"
|
"version" "7.22.20"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-validator-identifier" "^7.18.6"
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
"chalk" "^2.0.0"
|
"chalk" "^2.4.2"
|
||||||
"js-tokens" "^4.0.0"
|
"js-tokens" "^4.0.0"
|
||||||
|
|
||||||
"@babel/parser@^7.20.7", "@babel/parser@^7.21.4":
|
"@babel/parser@^7.22.15", "@babel/parser@^7.23.3":
|
||||||
"integrity" "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw=="
|
"integrity" "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz"
|
||||||
"version" "7.21.4"
|
"version" "7.23.3"
|
||||||
|
|
||||||
"@babel/plugin-syntax-jsx@^7.12.13":
|
"@babel/plugin-syntax-jsx@^7.12.13":
|
||||||
"integrity" "sha512-42OGssv9NPk4QHKVgIHlzeLgPOW5rGgfV5jzG90AhcXXIv6hu/eqj63w4VgvRxdvZY3AlYeDgPiSJ3BqAd1Y6Q=="
|
"integrity" "sha512-42OGssv9NPk4QHKVgIHlzeLgPOW5rGgfV5jzG90AhcXXIv6hu/eqj63w4VgvRxdvZY3AlYeDgPiSJ3BqAd1Y6Q=="
|
||||||
@@ -176,38 +174,38 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"regenerator-runtime" "^0.13.4"
|
"regenerator-runtime" "^0.13.4"
|
||||||
|
|
||||||
"@babel/template@^7.20.7":
|
"@babel/template@^7.22.15":
|
||||||
"integrity" "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw=="
|
"integrity" "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
|
||||||
"version" "7.20.7"
|
"version" "7.22.15"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.18.6"
|
"@babel/code-frame" "^7.22.13"
|
||||||
"@babel/parser" "^7.20.7"
|
"@babel/parser" "^7.22.15"
|
||||||
"@babel/types" "^7.20.7"
|
"@babel/types" "^7.22.15"
|
||||||
|
|
||||||
"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4":
|
"@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3":
|
||||||
"integrity" "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q=="
|
"integrity" "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz"
|
||||||
"version" "7.21.4"
|
"version" "7.23.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.21.4"
|
"@babel/code-frame" "^7.22.13"
|
||||||
"@babel/generator" "^7.21.4"
|
"@babel/generator" "^7.23.3"
|
||||||
"@babel/helper-environment-visitor" "^7.18.9"
|
"@babel/helper-environment-visitor" "^7.22.20"
|
||||||
"@babel/helper-function-name" "^7.21.0"
|
"@babel/helper-function-name" "^7.23.0"
|
||||||
"@babel/helper-hoist-variables" "^7.18.6"
|
"@babel/helper-hoist-variables" "^7.22.5"
|
||||||
"@babel/helper-split-export-declaration" "^7.18.6"
|
"@babel/helper-split-export-declaration" "^7.22.6"
|
||||||
"@babel/parser" "^7.21.4"
|
"@babel/parser" "^7.23.3"
|
||||||
"@babel/types" "^7.21.4"
|
"@babel/types" "^7.23.3"
|
||||||
"debug" "^4.1.0"
|
"debug" "^4.1.0"
|
||||||
"globals" "^11.1.0"
|
"globals" "^11.1.0"
|
||||||
|
|
||||||
"@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4":
|
"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3":
|
||||||
"integrity" "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA=="
|
"integrity" "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw=="
|
||||||
"resolved" "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz"
|
"resolved" "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz"
|
||||||
"version" "7.21.4"
|
"version" "7.23.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-string-parser" "^7.19.4"
|
"@babel/helper-string-parser" "^7.22.5"
|
||||||
"@babel/helper-validator-identifier" "^7.19.1"
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
"to-fast-properties" "^2.0.0"
|
"to-fast-properties" "^2.0.0"
|
||||||
|
|
||||||
"@chakra-ui/accordion@1.4.2":
|
"@chakra-ui/accordion@1.4.2":
|
||||||
@@ -896,33 +894,28 @@
|
|||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
"@jridgewell/trace-mapping" "^0.3.9"
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
"@jridgewell/resolve-uri@3.1.0":
|
"@jridgewell/resolve-uri@^3.1.0":
|
||||||
"integrity" "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
|
"integrity" "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA=="
|
||||||
"resolved" "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz"
|
"resolved" "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz"
|
||||||
"version" "3.1.0"
|
"version" "3.1.1"
|
||||||
|
|
||||||
"@jridgewell/set-array@^1.0.1":
|
"@jridgewell/set-array@^1.0.1":
|
||||||
"integrity" "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
|
"integrity" "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
|
||||||
"resolved" "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
|
"resolved" "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
|
||||||
"version" "1.1.2"
|
"version" "1.1.2"
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.10":
|
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
|
||||||
"integrity" "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
"integrity" "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
||||||
"resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
"resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
||||||
"version" "1.4.15"
|
"version" "1.4.15"
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@1.4.14":
|
|
||||||
"integrity" "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
|
||||||
"resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz"
|
|
||||||
"version" "1.4.14"
|
|
||||||
|
|
||||||
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
|
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
|
||||||
"integrity" "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA=="
|
"integrity" "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q=="
|
||||||
"resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz"
|
"resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz"
|
||||||
"version" "0.3.18"
|
"version" "0.3.20"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/resolve-uri" "3.1.0"
|
"@jridgewell/resolve-uri" "^3.1.0"
|
||||||
"@jridgewell/sourcemap-codec" "1.4.14"
|
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||||
|
|
||||||
"@popperjs/core@^2.9.3":
|
"@popperjs/core@^2.9.3":
|
||||||
"integrity" "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ=="
|
"integrity" "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ=="
|
||||||
@@ -1074,27 +1067,27 @@
|
|||||||
"cosmiconfig" "^6.0.0"
|
"cosmiconfig" "^6.0.0"
|
||||||
"resolve" "^1.12.0"
|
"resolve" "^1.12.0"
|
||||||
|
|
||||||
"browserslist@^4.21.3", "browserslist@>= 4.21.0":
|
"browserslist@^4.21.9", "browserslist@>= 4.21.0":
|
||||||
"integrity" "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w=="
|
"integrity" "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ=="
|
||||||
"resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz"
|
"resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz"
|
||||||
"version" "4.21.5"
|
"version" "4.22.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"caniuse-lite" "^1.0.30001449"
|
"caniuse-lite" "^1.0.30001541"
|
||||||
"electron-to-chromium" "^1.4.284"
|
"electron-to-chromium" "^1.4.535"
|
||||||
"node-releases" "^2.0.8"
|
"node-releases" "^2.0.13"
|
||||||
"update-browserslist-db" "^1.0.10"
|
"update-browserslist-db" "^1.0.13"
|
||||||
|
|
||||||
"callsites@^3.0.0":
|
"callsites@^3.0.0":
|
||||||
"integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
"integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
||||||
"resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
|
"resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
|
||||||
"version" "3.1.0"
|
"version" "3.1.0"
|
||||||
|
|
||||||
"caniuse-lite@^1.0.30001449":
|
"caniuse-lite@^1.0.30001541":
|
||||||
"integrity" "sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ=="
|
"integrity" "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw=="
|
||||||
"resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz"
|
"resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz"
|
||||||
"version" "1.0.30001480"
|
"version" "1.0.30001561"
|
||||||
|
|
||||||
"chalk@^2.0.0":
|
"chalk@^2.4.2":
|
||||||
"integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="
|
"integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="
|
||||||
"resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
|
"resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
|
||||||
"version" "2.4.2"
|
"version" "2.4.2"
|
||||||
@@ -1125,13 +1118,18 @@
|
|||||||
"resolved" "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz"
|
"resolved" "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.14.tgz"
|
||||||
"version" "1.0.14"
|
"version" "1.0.14"
|
||||||
|
|
||||||
"convert-source-map@^1.5.0", "convert-source-map@^1.7.0":
|
"convert-source-map@^1.5.0":
|
||||||
"integrity" "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA=="
|
"integrity" "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA=="
|
||||||
"resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz"
|
"resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz"
|
||||||
"version" "1.8.0"
|
"version" "1.8.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"safe-buffer" "~5.1.1"
|
"safe-buffer" "~5.1.1"
|
||||||
|
|
||||||
|
"convert-source-map@^2.0.0":
|
||||||
|
"integrity" "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
|
||||||
|
"resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz"
|
||||||
|
"version" "2.0.0"
|
||||||
|
|
||||||
"copy-to-clipboard@3.3.1":
|
"copy-to-clipboard@3.3.1":
|
||||||
"integrity" "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw=="
|
"integrity" "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw=="
|
||||||
"resolved" "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz"
|
"resolved" "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz"
|
||||||
@@ -1140,9 +1138,9 @@
|
|||||||
"toggle-selection" "^1.0.6"
|
"toggle-selection" "^1.0.6"
|
||||||
|
|
||||||
"core-js@^3.6.4":
|
"core-js@^3.6.4":
|
||||||
"integrity" "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ=="
|
"integrity" "sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ=="
|
||||||
"resolved" "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz"
|
"resolved" "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz"
|
||||||
"version" "3.30.1"
|
"version" "3.33.2"
|
||||||
|
|
||||||
"cosmiconfig@^6.0.0":
|
"cosmiconfig@^6.0.0":
|
||||||
"integrity" "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg=="
|
"integrity" "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg=="
|
||||||
@@ -1156,11 +1154,11 @@
|
|||||||
"yaml" "^1.7.2"
|
"yaml" "^1.7.2"
|
||||||
|
|
||||||
"cross-fetch@^3.0.4":
|
"cross-fetch@^3.0.4":
|
||||||
"integrity" "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw=="
|
"integrity" "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg=="
|
||||||
"resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz"
|
"resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz"
|
||||||
"version" "3.1.5"
|
"version" "3.1.8"
|
||||||
dependencies:
|
dependencies:
|
||||||
"node-fetch" "2.6.7"
|
"node-fetch" "^2.6.12"
|
||||||
|
|
||||||
"css-box-model@1.2.1":
|
"css-box-model@1.2.1":
|
||||||
"integrity" "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw=="
|
"integrity" "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw=="
|
||||||
@@ -1210,10 +1208,10 @@
|
|||||||
"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"
|
||||||
"version" "0.10.2"
|
"version" "0.10.2"
|
||||||
|
|
||||||
"electron-to-chromium@^1.4.284":
|
"electron-to-chromium@^1.4.535":
|
||||||
"integrity" "sha512-FRHZO+1tUNO4TOPXmlxetkoaIY8uwHzd1kKopK/Gx2SKn1L47wJXWD44wxP5CGRyyP98z/c8e1eBzJrgPeiBOg=="
|
"integrity" "sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw=="
|
||||||
"resolved" "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.365.tgz"
|
"resolved" "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.581.tgz"
|
||||||
"version" "1.4.365"
|
"version" "1.4.581"
|
||||||
|
|
||||||
"error-ex@^1.3.1":
|
"error-ex@^1.3.1":
|
||||||
"integrity" "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="
|
"integrity" "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="
|
||||||
@@ -1222,9 +1220,9 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"is-arrayish" "^0.2.1"
|
"is-arrayish" "^0.2.1"
|
||||||
|
|
||||||
"esbuild-darwin-arm64@0.14.9":
|
"esbuild-linux-64@0.14.9":
|
||||||
"integrity" "sha512-3ue+1T4FR5TaAu4/V1eFMG8Uwn0pgAwQZb/WwL1X78d5Cy8wOVQ67KNH1lsjU+y/9AcwMKZ9x0GGNxBB4a1Rbw=="
|
"integrity" "sha512-WoEI+R6/PLZAxS7XagfQMFgRtLUi5cjqqU9VCfo3tnWmAXh/wt8QtUfCVVCcXVwZLS/RNvI19CtfjlrJU61nOg=="
|
||||||
"resolved" "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.9.tgz"
|
"resolved" "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.9.tgz"
|
||||||
"version" "0.14.9"
|
"version" "0.14.9"
|
||||||
|
|
||||||
"esbuild@^0.14.9":
|
"esbuild@^0.14.9":
|
||||||
@@ -1405,9 +1403,9 @@
|
|||||||
"version" "3.7.6"
|
"version" "3.7.6"
|
||||||
|
|
||||||
"immutable@3.x.x || 4.x.x":
|
"immutable@3.x.x || 4.x.x":
|
||||||
"integrity" "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg=="
|
"integrity" "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA=="
|
||||||
"resolved" "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz"
|
"resolved" "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz"
|
||||||
"version" "4.3.0"
|
"version" "4.3.4"
|
||||||
|
|
||||||
"import-fresh@^3.1.0":
|
"import-fresh@^3.1.0":
|
||||||
"integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw=="
|
"integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw=="
|
||||||
@@ -1451,7 +1449,7 @@
|
|||||||
"resolved" "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz"
|
"resolved" "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz"
|
||||||
"version" "2.3.1"
|
"version" "2.3.1"
|
||||||
|
|
||||||
"json5@^2.2.2":
|
"json5@^2.2.3":
|
||||||
"integrity" "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
|
"integrity" "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
|
||||||
"resolved" "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
|
"resolved" "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
|
||||||
"version" "2.2.3"
|
"version" "2.2.3"
|
||||||
@@ -1497,17 +1495,17 @@
|
|||||||
"resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
"resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||||
"version" "2.1.2"
|
"version" "2.1.2"
|
||||||
|
|
||||||
"node-fetch@2.6.7":
|
"node-fetch@^2.6.12":
|
||||||
"integrity" "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ=="
|
"integrity" "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="
|
||||||
"resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz"
|
"resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
||||||
"version" "2.6.7"
|
"version" "2.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"whatwg-url" "^5.0.0"
|
"whatwg-url" "^5.0.0"
|
||||||
|
|
||||||
"node-releases@^2.0.8":
|
"node-releases@^2.0.13":
|
||||||
"integrity" "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w=="
|
"integrity" "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
|
||||||
"resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz"
|
"resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz"
|
||||||
"version" "2.0.10"
|
"version" "2.0.13"
|
||||||
|
|
||||||
"object-assign@^4.1.0", "object-assign@^4.1.1":
|
"object-assign@^4.1.0", "object-assign@^4.1.1":
|
||||||
"integrity" "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
"integrity" "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||||
@@ -1739,10 +1737,10 @@
|
|||||||
"loose-envify" "^1.1.0"
|
"loose-envify" "^1.1.0"
|
||||||
"object-assign" "^4.1.1"
|
"object-assign" "^4.1.1"
|
||||||
|
|
||||||
"semver@^6.3.0":
|
"semver@^6.3.1":
|
||||||
"integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
"integrity" "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
|
||||||
"resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
|
"resolved" "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
||||||
"version" "6.3.0"
|
"version" "6.3.1"
|
||||||
|
|
||||||
"setimmediate@^1.0.5":
|
"setimmediate@^1.0.5":
|
||||||
"integrity" "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
|
"integrity" "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
|
||||||
@@ -1815,19 +1813,19 @@
|
|||||||
"version" "4.5.4"
|
"version" "4.5.4"
|
||||||
|
|
||||||
"ua-parser-js@^0.7.18":
|
"ua-parser-js@^0.7.18":
|
||||||
"integrity" "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g=="
|
"integrity" "sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA=="
|
||||||
"resolved" "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz"
|
"resolved" "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.37.tgz"
|
||||||
"version" "0.7.35"
|
"version" "0.7.37"
|
||||||
|
|
||||||
"uc.micro@^1.0.1":
|
"uc.micro@^1.0.1":
|
||||||
"integrity" "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
|
"integrity" "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
|
||||||
"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"
|
||||||
"version" "1.0.6"
|
"version" "1.0.6"
|
||||||
|
|
||||||
"update-browserslist-db@^1.0.10":
|
"update-browserslist-db@^1.0.13":
|
||||||
"integrity" "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA=="
|
"integrity" "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg=="
|
||||||
"resolved" "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz"
|
"resolved" "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz"
|
||||||
"version" "1.0.11"
|
"version" "1.0.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"escalade" "^3.1.1"
|
"escalade" "^3.1.1"
|
||||||
"picocolors" "^1.0.0"
|
"picocolors" "^1.0.0"
|
||||||
|
25
server/authenticators/providers/providers.go
Normal file
25
server/authenticators/providers/providers.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package providers
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// AuthenticatorConfig defines authenticator config
|
||||||
|
type AuthenticatorConfig struct {
|
||||||
|
// ScannerImage is the base64 of QR code image
|
||||||
|
ScannerImage string
|
||||||
|
// Secrets is the secret key
|
||||||
|
Secret string
|
||||||
|
// RecoveryCode is the list of recovery codes
|
||||||
|
RecoveryCodes []string
|
||||||
|
// RecoveryCodeMap is the map of recovery codes
|
||||||
|
RecoveryCodeMap map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider defines authenticators provider
|
||||||
|
type Provider interface {
|
||||||
|
// Generate totp: to generate totp, store secret into db and returns base64 of QR code image
|
||||||
|
Generate(ctx context.Context, id string) (*AuthenticatorConfig, error)
|
||||||
|
// Validate totp: user passcode with secret stored in our db
|
||||||
|
Validate(ctx context.Context, passcode string, userID string) (bool, error)
|
||||||
|
// ValidateRecoveryCode totp: allows user to validate using recovery code incase if they lost their device
|
||||||
|
ValidateRecoveryCode(ctx context.Context, recoveryCode, userID string) (bool, error)
|
||||||
|
}
|
23
server/authenticators/providers/totp/provider.go
Normal file
23
server/authenticators/providers/totp/provider.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package totp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type provider struct {
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOTPConfig defines totp config
|
||||||
|
type TOTPConfig struct {
|
||||||
|
ScannerImage string
|
||||||
|
Secret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProvider returns a new totp provider
|
||||||
|
func NewProvider() (*provider, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
return &provider{
|
||||||
|
ctx: ctx,
|
||||||
|
}, nil
|
||||||
|
}
|
151
server/authenticators/providers/totp/totp.go
Normal file
151
server/authenticators/providers/totp/totp.go
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
package totp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"image/png"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/pquerna/otp/totp"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/authenticators/providers"
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/crypto"
|
||||||
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
"github.com/authorizerdev/authorizer/server/refs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate generates a Time-Based One-Time Password (TOTP) for a user and returns the base64-encoded QR code for frontend display.
|
||||||
|
func (p *provider) Generate(ctx context.Context, id string) (*providers.AuthenticatorConfig, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
//get user details
|
||||||
|
user, err := db.Provider.GetUserByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// generate totp, Authenticators hash is valid for 30 seconds
|
||||||
|
key, err := totp.Generate(totp.GenerateOpts{
|
||||||
|
Issuer: "authorizer",
|
||||||
|
AccountName: refs.StringValue(user.Email),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//generating image for key and encoding to base64 for displaying in frontend
|
||||||
|
img, err := key.Image(200, 200)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
png.Encode(&buf, img)
|
||||||
|
encodedText := crypto.EncryptB64(buf.String())
|
||||||
|
secret := key.Secret()
|
||||||
|
recoveryCodes := []string{}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
recoveryCodes = append(recoveryCodes, uuid.NewString())
|
||||||
|
}
|
||||||
|
// Converting recoveryCodes to string
|
||||||
|
recoverCodesMap := map[string]bool{}
|
||||||
|
for i := 0; i < len(recoveryCodes); i++ {
|
||||||
|
recoverCodesMap[recoveryCodes[i]] = false
|
||||||
|
}
|
||||||
|
// Converting recoveryCodesMap to string
|
||||||
|
jsonData, err := json.Marshal(recoverCodesMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
recoveryCodesString := string(jsonData)
|
||||||
|
totpModel := &models.Authenticator{
|
||||||
|
Secret: secret,
|
||||||
|
RecoveryCodes: refs.NewStringRef(recoveryCodesString),
|
||||||
|
UserID: user.ID,
|
||||||
|
Method: constants.EnvKeyTOTPAuthenticator,
|
||||||
|
}
|
||||||
|
authenticator, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get authenticator details by user id, creating new record: ", err)
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
if authenticator == nil {
|
||||||
|
// if authenticator is nil then create new authenticator
|
||||||
|
_, err = db.Provider.AddAuthenticator(ctx, totpModel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
authenticator.Secret = secret
|
||||||
|
authenticator.RecoveryCodes = refs.NewStringRef(recoveryCodesString)
|
||||||
|
// if authenticator is not nil then update authenticator
|
||||||
|
_, err = db.Provider.UpdateAuthenticator(ctx, authenticator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &providers.AuthenticatorConfig{
|
||||||
|
ScannerImage: encodedText,
|
||||||
|
Secret: secret,
|
||||||
|
RecoveryCodes: recoveryCodes,
|
||||||
|
RecoveryCodeMap: recoverCodesMap,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates a Time-Based One-Time Password (TOTP) against the stored TOTP secret for a user.
|
||||||
|
func (p *provider) Validate(ctx context.Context, passcode string, userID string) (bool, error) {
|
||||||
|
// get totp details
|
||||||
|
totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, userID, constants.EnvKeyTOTPAuthenticator)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// validate totp
|
||||||
|
status := totp.Validate(passcode, totpModel.Secret)
|
||||||
|
// checks if user not signed in for totp and totp code is correct then VerifiedAt will be stored in db
|
||||||
|
if totpModel.VerifiedAt == nil && status {
|
||||||
|
timeNow := time.Now().Unix()
|
||||||
|
totpModel.VerifiedAt = &timeNow
|
||||||
|
_, err = db.Provider.UpdateAuthenticator(ctx, totpModel)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateRecoveryCode validates a Time-Based One-Time Password (TOTP) recovery code against the stored TOTP recovery code for a user.
|
||||||
|
func (p *provider) ValidateRecoveryCode(ctx context.Context, recoveryCode, userID string) (bool, error) {
|
||||||
|
// get totp details
|
||||||
|
totpModel, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, userID, constants.EnvKeyTOTPAuthenticator)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// convert recoveryCodes to map
|
||||||
|
recoveryCodesMap := map[string]bool{}
|
||||||
|
err = json.Unmarshal([]byte(refs.StringValue(totpModel.RecoveryCodes)), &recoveryCodesMap)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// check if recovery code is valid
|
||||||
|
if val, ok := recoveryCodesMap[recoveryCode]; !ok {
|
||||||
|
return false, fmt.Errorf("invalid recovery code")
|
||||||
|
} else if val {
|
||||||
|
return false, fmt.Errorf("recovery code already used")
|
||||||
|
}
|
||||||
|
// update recovery code map
|
||||||
|
recoveryCodesMap[recoveryCode] = true
|
||||||
|
// convert recoveryCodesMap to string
|
||||||
|
jsonData, err := json.Marshal(recoveryCodesMap)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
recoveryCodesString := string(jsonData)
|
||||||
|
totpModel.RecoveryCodes = refs.NewStringRef(recoveryCodesString)
|
||||||
|
// update recovery code map in db
|
||||||
|
_, err = db.Provider.UpdateAuthenticator(ctx, totpModel)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
26
server/authenticators/totp_store.go
Normal file
26
server/authenticators/totp_store.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package authenticators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/authorizerdev/authorizer/server/authenticators/providers"
|
||||||
|
"github.com/authorizerdev/authorizer/server/authenticators/providers/totp"
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provider is the global authenticators provider.
|
||||||
|
var Provider providers.Provider
|
||||||
|
|
||||||
|
// InitTOTPStore initializes the TOTP authenticator store if it's not disabled in the environment variables.
|
||||||
|
// It sets the global Provider variable to a new TOTP provider.
|
||||||
|
func InitTOTPStore() error {
|
||||||
|
var err error
|
||||||
|
isTOTPEnvServiceDisabled, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
|
||||||
|
|
||||||
|
if !isTOTPEnvServiceDisabled {
|
||||||
|
Provider, err = totp.NewProvider()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@@ -19,8 +19,12 @@ const (
|
|||||||
AuthRecipeMethodLinkedIn = "linkedin"
|
AuthRecipeMethodLinkedIn = "linkedin"
|
||||||
// AuthRecipeMethodApple is the apple auth method
|
// AuthRecipeMethodApple is the apple auth method
|
||||||
AuthRecipeMethodApple = "apple"
|
AuthRecipeMethodApple = "apple"
|
||||||
|
// AuthRecipeMethodDiscord is the discord auth method
|
||||||
|
AuthRecipeMethodDiscord = "discord"
|
||||||
// AuthRecipeMethodTwitter is the twitter auth method
|
// AuthRecipeMethodTwitter is the twitter auth method
|
||||||
AuthRecipeMethodTwitter = "twitter"
|
AuthRecipeMethodTwitter = "twitter"
|
||||||
// AuthRecipeMethodMicrosoft is the microsoft auth method
|
// AuthRecipeMethodMicrosoft is the microsoft auth method
|
||||||
AuthRecipeMethodMicrosoft = "microsoft"
|
AuthRecipeMethodMicrosoft = "microsoft"
|
||||||
|
// AuthRecipeMethodTwitch is the twitch auth method
|
||||||
|
AuthRecipeMethodTwitch = "twitch"
|
||||||
)
|
)
|
||||||
|
7
server/constants/authenticator_method.go
Normal file
7
server/constants/authenticator_method.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
// Authenticators Methods
|
||||||
|
const (
|
||||||
|
// EnvKeyTOTPAuthenticator key for env variable TOTP
|
||||||
|
EnvKeyTOTPAuthenticator = "totp"
|
||||||
|
)
|
@@ -5,4 +5,6 @@ const (
|
|||||||
AppCookieName = "cookie"
|
AppCookieName = "cookie"
|
||||||
// AdminCookieName is the name of the cookie that is used to store the admin token
|
// AdminCookieName is the name of the cookie that is used to store the admin token
|
||||||
AdminCookieName = "authorizer-admin"
|
AdminCookieName = "authorizer-admin"
|
||||||
|
// MfaCookieName is the name of the cookie that is used to store the mfa session
|
||||||
|
MfaCookieName = "mfa"
|
||||||
)
|
)
|
||||||
|
@@ -5,6 +5,8 @@ const (
|
|||||||
DbTypePostgres = "postgres"
|
DbTypePostgres = "postgres"
|
||||||
// DbTypeSqlite is the sqlite database type
|
// DbTypeSqlite is the sqlite database type
|
||||||
DbTypeSqlite = "sqlite"
|
DbTypeSqlite = "sqlite"
|
||||||
|
// DbTypeLibSQL is the libsql / Turso database type
|
||||||
|
DbTypeLibSQL = "libsql"
|
||||||
// DbTypeMysql is the mysql database type
|
// DbTypeMysql is the mysql database type
|
||||||
DbTypeMysql = "mysql"
|
DbTypeMysql = "mysql"
|
||||||
// DbTypeSqlserver is the sqlserver database type
|
// DbTypeSqlserver is the sqlserver database type
|
||||||
|
@@ -108,6 +108,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"
|
||||||
|
// EnvKeyDiscordClientID key for env variable DISCORD_CLIENT_ID
|
||||||
|
EnvKeyDiscordClientID = "DISCORD_CLIENT_ID"
|
||||||
|
// EnvKeyDiscordClientSecret key for env variable DISCORD_CLIENT_SECRET
|
||||||
|
EnvKeyDiscordClientSecret = "DISCORD_CLIENT_SECRET"
|
||||||
// EnvKeyTwitterClientID key for env variable TWITTER_CLIENT_ID
|
// EnvKeyTwitterClientID key for env variable TWITTER_CLIENT_ID
|
||||||
EnvKeyTwitterClientID = "TWITTER_CLIENT_ID"
|
EnvKeyTwitterClientID = "TWITTER_CLIENT_ID"
|
||||||
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET
|
// EnvKeyTwitterClientSecret key for env variable TWITTER_CLIENT_SECRET
|
||||||
@@ -118,6 +122,10 @@ const (
|
|||||||
EnvKeyMicrosoftActiveDirectoryTenantID = "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"
|
EnvKeyMicrosoftActiveDirectoryTenantID = "MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"
|
||||||
// EnvKeyMicrosoftClientSecret key for env variable MICROSOFT_CLIENT_SECRET
|
// EnvKeyMicrosoftClientSecret key for env variable MICROSOFT_CLIENT_SECRET
|
||||||
EnvKeyMicrosoftClientSecret = "MICROSOFT_CLIENT_SECRET"
|
EnvKeyMicrosoftClientSecret = "MICROSOFT_CLIENT_SECRET"
|
||||||
|
// EnvKeyTwitchClientID key for env variable TWITCH_CLIENT_ID
|
||||||
|
EnvKeyTwitchClientID = "TWITCH_CLIENT_ID"
|
||||||
|
// EnvKeyTwitchClientSecret key for env variable TWITCH_CLIENT_SECRET
|
||||||
|
EnvKeyTwitchClientSecret = "TWITCH_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
|
||||||
@@ -160,9 +168,18 @@ const (
|
|||||||
// EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION
|
// EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION
|
||||||
// this variable is used to completely disable multi factor authentication. It will have no effect on profile preference
|
// this variable is used to completely disable multi factor authentication. It will have no effect on profile preference
|
||||||
EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION"
|
EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION"
|
||||||
|
// EnvKeyDisableTOTPLogin is key for env variable DISABLE_TOTP_LOGIN
|
||||||
|
// this variable is used to completely disable totp verification
|
||||||
|
EnvKeyDisableTOTPLogin = "DISABLE_TOTP_LOGIN"
|
||||||
|
// EnvKeyDisableMailOTPLogin is key for env variable DISABLE_MAIL_OTP_LOGIN
|
||||||
|
// this variable is used to completely disable totp verification
|
||||||
|
EnvKeyDisableMailOTPLogin = "DISABLE_MAIL_OTP_LOGIN"
|
||||||
// EnvKeyDisablePhoneVerification is key for env variable DISABLE_PHONE_VERIFICATION
|
// EnvKeyDisablePhoneVerification is key for env variable DISABLE_PHONE_VERIFICATION
|
||||||
// this variable is used to disable phone verification
|
// this variable is used to disable phone verification
|
||||||
EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION"
|
EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION"
|
||||||
|
// EnvKeyDisablePlayGround is key for env variable DISABLE_PLAYGROUND
|
||||||
|
// this variable will disable or enable playground use in dashboard
|
||||||
|
EnvKeyDisablePlayGround = "DISABLE_PLAYGROUND"
|
||||||
|
|
||||||
// Slice variables
|
// Slice variables
|
||||||
// EnvKeyRoles key for env variable ROLES
|
// EnvKeyRoles key for env variable ROLES
|
||||||
|
@@ -16,4 +16,7 @@ const (
|
|||||||
ResponseTypeToken = "token"
|
ResponseTypeToken = "token"
|
||||||
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
|
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
|
||||||
ResponseTypeIDToken = "id_token"
|
ResponseTypeIDToken = "id_token"
|
||||||
|
|
||||||
|
// Constant indicating the "signup" screen hint for customizing authentication process and redirect to a signup page.
|
||||||
|
ScreenHintSignUp = "signup"
|
||||||
)
|
)
|
||||||
|
@@ -17,6 +17,7 @@ const (
|
|||||||
|
|
||||||
TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username"
|
TwitterUserInfoURL = "https://api.twitter.com/2/users/me?user.fields=id,name,profile_image_url,username"
|
||||||
|
|
||||||
|
DiscordUserInfoURL = "https://discord.com/api/oauth2/@me"
|
||||||
// Get microsoft user info.
|
// Get microsoft user info.
|
||||||
// Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo
|
// Ref: https://learn.microsoft.com/en-us/azure/active-directory/develop/userinfo
|
||||||
MicrosoftUserInfoURL = "https://graph.microsoft.com/oidc/userinfo"
|
MicrosoftUserInfoURL = "https://graph.microsoft.com/oidc/userinfo"
|
||||||
|
@@ -15,4 +15,6 @@ const (
|
|||||||
UserAccessEnabledWebhookEvent = `user.access_enabled`
|
UserAccessEnabledWebhookEvent = `user.access_enabled`
|
||||||
// UserDeletedWebhookEvent name for user deleted event
|
// UserDeletedWebhookEvent name for user deleted event
|
||||||
UserDeletedWebhookEvent = `user.deleted`
|
UserDeletedWebhookEvent = `user.deleted`
|
||||||
|
// UserDeactivatedWebhookEvent name for user deactivated event
|
||||||
|
UserDeactivatedWebhookEvent = `user.deactivated`
|
||||||
)
|
)
|
||||||
|
89
server/cookie/mfa_session.go
Normal file
89
server/cookie/mfa_session.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package cookie
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
|
"github.com/authorizerdev/authorizer/server/parsers"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetMfaSession sets the mfa session cookie in the response
|
||||||
|
func SetMfaSession(gc *gin.Context, sessionID string) {
|
||||||
|
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error while getting app cookie secure from env variable: %v", err)
|
||||||
|
appCookieSecure = true
|
||||||
|
}
|
||||||
|
|
||||||
|
secure := appCookieSecure
|
||||||
|
httpOnly := appCookieSecure
|
||||||
|
hostname := parsers.GetHost(gc)
|
||||||
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
|
domain := parsers.GetDomainName(hostname)
|
||||||
|
if domain != "localhost" {
|
||||||
|
domain = "." + domain
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since app cookie can come from cross site it becomes important to set this in lax mode when insecure.
|
||||||
|
// Example person using custom UI on their app domain and making request to authorizer domain.
|
||||||
|
// For more information check:
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
|
||||||
|
// https://github.com/gin-gonic/gin/blob/master/context.go#L86
|
||||||
|
// TODO add ability to sameSite = none / strict from dashboard
|
||||||
|
if !appCookieSecure {
|
||||||
|
gc.SetSameSite(http.SameSiteLaxMode)
|
||||||
|
} else {
|
||||||
|
gc.SetSameSite(http.SameSiteNoneMode)
|
||||||
|
}
|
||||||
|
// TODO allow configuring from dashboard
|
||||||
|
age := 60
|
||||||
|
|
||||||
|
gc.SetCookie(constants.MfaCookieName+"_session", sessionID, age, "/", host, secure, httpOnly)
|
||||||
|
gc.SetCookie(constants.MfaCookieName+"_session_domain", sessionID, age, "/", domain, secure, httpOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMfaSession deletes the mfa session cookies to expire
|
||||||
|
func DeleteMfaSession(gc *gin.Context) {
|
||||||
|
appCookieSecure, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyAppCookieSecure)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error while getting app cookie secure from env variable: %v", err)
|
||||||
|
appCookieSecure = true
|
||||||
|
}
|
||||||
|
|
||||||
|
secure := appCookieSecure
|
||||||
|
httpOnly := appCookieSecure
|
||||||
|
hostname := parsers.GetHost(gc)
|
||||||
|
host, _ := parsers.GetHostParts(hostname)
|
||||||
|
domain := parsers.GetDomainName(hostname)
|
||||||
|
if domain != "localhost" {
|
||||||
|
domain = "." + domain
|
||||||
|
}
|
||||||
|
|
||||||
|
gc.SetSameSite(http.SameSiteNoneMode)
|
||||||
|
gc.SetCookie(constants.MfaCookieName+"_session", "", -1, "/", host, secure, httpOnly)
|
||||||
|
gc.SetCookie(constants.MfaCookieName+"_session_domain", "", -1, "/", domain, secure, httpOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMfaSession gets the mfa session cookie from context
|
||||||
|
func GetMfaSession(gc *gin.Context) (string, error) {
|
||||||
|
var cookie *http.Cookie
|
||||||
|
var err error
|
||||||
|
cookie, err = gc.Request.Cookie(constants.MfaCookieName + "_session")
|
||||||
|
if err != nil {
|
||||||
|
cookie, err = gc.Request.Cookie(constants.MfaCookieName + "_session_domain")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedValue, err := url.PathUnescape(cookie.Value)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return decodedValue, nil
|
||||||
|
}
|
@@ -1,7 +1,9 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
@@ -125,12 +127,27 @@ func EncryptEnvData(data map[string]interface{}) (string, error) {
|
|||||||
return EncryptB64(string(encryptedConfig)), nil
|
return EncryptB64(string(encryptedConfig)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getSHA256 calculates the SHA-256 hash of a string
|
||||||
|
func getSHA256(input string) string {
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write([]byte(input))
|
||||||
|
return hex.EncodeToString(hash.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyPassword compares a stored hashed password with a user-provided password
|
||||||
|
func VerifyPassword(storedHashedPassword, userProvidedPassword string) error {
|
||||||
|
// CompareHashAndPassword returns nil on success
|
||||||
|
passwordSHA256 := getSHA256(userProvidedPassword)
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(storedHashedPassword), []byte(passwordSHA256))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// EncryptPassword is used for encrypting password
|
// EncryptPassword is used for encrypting password
|
||||||
func EncryptPassword(password string) (string, error) {
|
func EncryptPassword(password string) (string, error) {
|
||||||
pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
passwordSHA256 := getSHA256(password)
|
||||||
|
pw, err := bcrypt.GenerateFromPassword([]byte(passwordSHA256), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(pw), nil
|
return string(pw), nil
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,9 @@ package crypto
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
@@ -116,3 +118,24 @@ func AsRSAStr(privateKey *rsa.PrivateKey, publickKey *rsa.PublicKey) (string, st
|
|||||||
|
|
||||||
return privParsedPem, pubParsedPem, nil
|
return privParsedPem, pubParsedPem, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EncryptRSA(message string, key rsa.PublicKey) (string, error) {
|
||||||
|
label := []byte("OAEP Encrypted")
|
||||||
|
rng := rand.Reader
|
||||||
|
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rng, &key, []byte(message), label)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecryptRSA(cipherText string, privateKey rsa.PrivateKey) (string, error) {
|
||||||
|
ct, _ := base64.StdEncoding.DecodeString(cipherText)
|
||||||
|
label := []byte("OAEP Encrypted")
|
||||||
|
rng := rand.Reader
|
||||||
|
plaintext, err := rsa.DecryptOAEP(sha256.New(), rng, &privateKey, ct, label)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(plaintext), nil
|
||||||
|
}
|
||||||
|
@@ -37,7 +37,6 @@ func InitDB() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isArangoDB {
|
if isArangoDB {
|
||||||
log.Info("Initializing ArangoDB Driver")
|
log.Info("Initializing ArangoDB Driver")
|
||||||
Provider, err = arangodb.NewProvider()
|
Provider, err = arangodb.NewProvider()
|
||||||
|
16
server/db/models/authenticators.go
Normal file
16
server/db/models/authenticators.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
// Note: any change here should be reflected in providers/casandra/provider.go as it does not have model support in collection creation
|
||||||
|
|
||||||
|
// Authenticators model for db
|
||||||
|
type Authenticator struct {
|
||||||
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||||
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||||
|
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"`
|
||||||
|
Method string `json:"method" bson:"method" cql:"method" dynamo:"method"`
|
||||||
|
Secret string `json:"secret" bson:"secret" cql:"secret" dynamo:"secret"`
|
||||||
|
RecoveryCodes *string `json:"recovery_codes" bson:"recovery_codes" cql:"recovery_codes" dynamo:"recovery_codes"`
|
||||||
|
VerifiedAt *int64 `json:"verified_at" bson:"verified_at" cql:"verified_at" dynamo:"verified_at"`
|
||||||
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
// Collections / Tables available for authorizer in the database
|
// CollectionList / Tables available for authorizer in the database
|
||||||
type CollectionList struct {
|
type CollectionList struct {
|
||||||
User string
|
User string
|
||||||
VerificationRequest string
|
VerificationRequest string
|
||||||
@@ -11,6 +11,7 @@ type CollectionList struct {
|
|||||||
EmailTemplate string
|
EmailTemplate string
|
||||||
OTP string
|
OTP string
|
||||||
SMSVerificationRequest string
|
SMSVerificationRequest string
|
||||||
|
Authenticators string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -27,5 +28,6 @@ var (
|
|||||||
EmailTemplate: Prefix + "email_templates",
|
EmailTemplate: Prefix + "email_templates",
|
||||||
OTP: Prefix + "otps",
|
OTP: Prefix + "otps",
|
||||||
SMSVerificationRequest: Prefix + "sms_verification_requests",
|
SMSVerificationRequest: Prefix + "sms_verification_requests",
|
||||||
|
Authenticators: Prefix + "authenticators",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@@ -15,7 +15,7 @@ type User struct {
|
|||||||
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
|
||||||
|
|
||||||
Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
|
Email *string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
|
||||||
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at" dynamo:"email_verified_at"`
|
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at" dynamo:"email_verified_at"`
|
||||||
Password *string `json:"password" bson:"password" cql:"password" dynamo:"password"`
|
Password *string `json:"password" bson:"password" cql:"password" dynamo:"password"`
|
||||||
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods" dynamo:"signup_methods"`
|
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods" dynamo:"signup_methods"`
|
||||||
@@ -33,12 +33,14 @@ type User struct {
|
|||||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled" dynamo:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled" dynamo:"is_multi_factor_auth_enabled"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
|
||||||
|
AppData *string `json:"app_data" bson:"app_data" cql:"app_data" dynamo:"app_data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) AsAPIUser() *model.User {
|
func (user *User) AsAPIUser() *model.User {
|
||||||
isEmailVerified := user.EmailVerifiedAt != nil
|
isEmailVerified := user.EmailVerifiedAt != nil
|
||||||
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
|
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
|
||||||
|
appDataMap := make(map[string]interface{})
|
||||||
|
json.Unmarshal([]byte(refs.StringValue(user.AppData)), &appDataMap)
|
||||||
// id := user.ID
|
// id := user.ID
|
||||||
// if strings.Contains(id, Collections.User+"/") {
|
// if strings.Contains(id, Collections.User+"/") {
|
||||||
// id = strings.TrimPrefix(id, Collections.User+"/")
|
// id = strings.TrimPrefix(id, Collections.User+"/")
|
||||||
@@ -52,7 +54,7 @@ func (user *User) AsAPIUser() *model.User {
|
|||||||
FamilyName: user.FamilyName,
|
FamilyName: user.FamilyName,
|
||||||
MiddleName: user.MiddleName,
|
MiddleName: user.MiddleName,
|
||||||
Nickname: user.Nickname,
|
Nickname: user.Nickname,
|
||||||
PreferredUsername: refs.NewStringRef(user.Email),
|
PreferredUsername: user.Email,
|
||||||
Gender: user.Gender,
|
Gender: user.Gender,
|
||||||
Birthdate: user.Birthdate,
|
Birthdate: user.Birthdate,
|
||||||
PhoneNumber: user.PhoneNumber,
|
PhoneNumber: user.PhoneNumber,
|
||||||
@@ -63,6 +65,7 @@ func (user *User) AsAPIUser() *model.User {
|
|||||||
IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled,
|
IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled,
|
||||||
CreatedAt: refs.NewInt64Ref(user.CreatedAt),
|
CreatedAt: refs.NewInt64Ref(user.CreatedAt),
|
||||||
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
|
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
|
||||||
|
AppData: appDataMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
78
server/db/providers/arangodb/authenticator.go
Normal file
78
server/db/providers/arangodb/authenticator.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package arangodb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
arangoDriver "github.com/arangodb/go-driver"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
||||||
|
if exists != nil {
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
if authenticators.ID == "" {
|
||||||
|
authenticators.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticators.Key = authenticators.ID
|
||||||
|
authenticators.CreatedAt = time.Now().Unix()
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
authenticatorsCollection, _ := p.db.Collection(ctx, models.Collections.Authenticators)
|
||||||
|
meta, err := authenticatorsCollection.CreateDocument(arangoDriver.WithOverwrite(ctx), authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
authenticators.Key = meta.Key
|
||||||
|
authenticators.ID = meta.ID.String()
|
||||||
|
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
collection, _ := p.db.Collection(ctx, models.Collections.Authenticators)
|
||||||
|
meta, err := collection.UpdateDocument(ctx, authenticators.Key, authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticators.Key = meta.Key
|
||||||
|
authenticators.ID = meta.ID.String()
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
||||||
|
var authenticators *models.Authenticator
|
||||||
|
query := fmt.Sprintf("FOR d in %s FILTER d.user_id == @user_id AND d.method == @method LIMIT 1 RETURN d", models.Collections.Authenticators)
|
||||||
|
bindVars := map[string]interface{}{
|
||||||
|
"user_id": userId,
|
||||||
|
"method": authenticatorType,
|
||||||
|
}
|
||||||
|
cursor, err := p.db.Query(ctx, query, bindVars)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
defer cursor.Close()
|
||||||
|
for {
|
||||||
|
if !cursor.HasMore() {
|
||||||
|
if authenticators == nil {
|
||||||
|
return authenticators, fmt.Errorf("authenticator not found")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
_, err := cursor.ReadDocument(ctx, &authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
@@ -186,6 +186,7 @@ func NewProvider() (*provider, error) {
|
|||||||
webhookLogCollection.EnsureHashIndex(ctx, []string{"webhook_id"}, &arangoDriver.EnsureHashIndexOptions{
|
webhookLogCollection.EnsureHashIndex(ctx, []string{"webhook_id"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
emailTemplateCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.EmailTemplate)
|
emailTemplateCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.EmailTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -204,6 +205,7 @@ func NewProvider() (*provider, error) {
|
|||||||
Unique: true,
|
Unique: true,
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
otpCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.OTP)
|
otpCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.OTP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -222,6 +224,26 @@ func NewProvider() (*provider, error) {
|
|||||||
Unique: true,
|
Unique: true,
|
||||||
Sparse: true,
|
Sparse: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//authenticators table define
|
||||||
|
authenticatorsCollectionExists, err := arangodb.CollectionExists(ctx, models.Collections.Authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !authenticatorsCollectionExists {
|
||||||
|
_, err = arangodb.CreateCollection(ctx, models.Collections.Authenticators, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
authenticatorsCollection, err := arangodb.Collection(ctx, models.Collections.Authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
authenticatorsCollection.EnsureHashIndex(ctx, []string{"user_id"}, &arangoDriver.EnsureHashIndexOptions{
|
||||||
|
Sparse: true,
|
||||||
|
})
|
||||||
|
|
||||||
return &provider{
|
return &provider{
|
||||||
db: arangodb,
|
db: arangodb,
|
||||||
}, err
|
}, err
|
||||||
|
133
server/db/providers/cassandradb/authenticator.go
Normal file
133
server/db/providers/cassandradb/authenticator.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package cassandradb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gocql/gocql"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
||||||
|
if exists != nil {
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if authenticators.ID == "" {
|
||||||
|
authenticators.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticators.CreatedAt = time.Now().Unix()
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
bytes, err := json.Marshal(authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
|
||||||
|
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
|
||||||
|
decoder.UseNumber()
|
||||||
|
authenticatorsMap := map[string]interface{}{}
|
||||||
|
err = decoder.Decode(&authenticatorsMap)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := "("
|
||||||
|
values := "("
|
||||||
|
for key, value := range authenticatorsMap {
|
||||||
|
if value != nil {
|
||||||
|
if key == "_id" {
|
||||||
|
fields += "id,"
|
||||||
|
} else {
|
||||||
|
fields += key + ","
|
||||||
|
}
|
||||||
|
|
||||||
|
valueType := reflect.TypeOf(value)
|
||||||
|
if valueType.Name() == "string" {
|
||||||
|
values += fmt.Sprintf("'%s',", value.(string))
|
||||||
|
} else {
|
||||||
|
values += fmt.Sprintf("%v,", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = fields[:len(fields)-1] + ")"
|
||||||
|
values = values[:len(values)-1] + ")"
|
||||||
|
|
||||||
|
query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.Authenticators, fields, values)
|
||||||
|
err = p.db.Query(query).Exec()
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
bytes, err := json.Marshal(authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
|
||||||
|
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
|
||||||
|
decoder.UseNumber()
|
||||||
|
authenticatorsMap := map[string]interface{}{}
|
||||||
|
err = decoder.Decode(&authenticatorsMap)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFields := ""
|
||||||
|
for key, value := range authenticatorsMap {
|
||||||
|
if key == "_id" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == "_key" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == nil {
|
||||||
|
updateFields += fmt.Sprintf("%s = null, ", key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
valueType := reflect.TypeOf(value)
|
||||||
|
if valueType.Name() == "string" {
|
||||||
|
updateFields += fmt.Sprintf("%s = '%s', ", key, value.(string))
|
||||||
|
} else {
|
||||||
|
updateFields += fmt.Sprintf("%s = %v, ", key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateFields = strings.Trim(updateFields, " ")
|
||||||
|
updateFields = strings.TrimSuffix(updateFields, ",")
|
||||||
|
|
||||||
|
query := fmt.Sprintf("UPDATE %s SET %s WHERE id = '%s'", KeySpace+"."+models.Collections.Authenticators, updateFields, authenticators.ID)
|
||||||
|
err = p.db.Query(query).Exec()
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
||||||
|
var authenticators models.Authenticator
|
||||||
|
query := fmt.Sprintf("SELECT id, user_id, method, secret, recovery_codes, verified_at, created_at, updated_at FROM %s WHERE user_id = '%s' AND method = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.Authenticators, userId, authenticatorType)
|
||||||
|
err := p.db.Query(query).Consistency(gocql.One).Scan(&authenticators.ID, &authenticators.UserID, &authenticators.Method, &authenticators.Secret, &authenticators.RecoveryCodes, &authenticators.VerifiedAt, &authenticators.CreatedAt, &authenticators.UpdatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &authenticators, nil
|
||||||
|
}
|
@@ -261,12 +261,26 @@ func NewProvider() (*provider, error) {
|
|||||||
log.Debug("Failed to alter table as column exists: ", err)
|
log.Debug("Failed to alter table as column exists: ", err)
|
||||||
// continue
|
// continue
|
||||||
}
|
}
|
||||||
|
// Add app_data column to users table
|
||||||
|
appDataAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (app_data text);`, KeySpace, models.Collections.User)
|
||||||
|
err = session.Query(appDataAlterQuery).Exec()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to alter user table as app_data column exists: ", err)
|
||||||
|
// continue
|
||||||
|
}
|
||||||
// Add phone number index
|
// Add phone number index
|
||||||
otpIndexQueryPhoneNumber := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_otp_phone_number ON %s.%s (phone_number)", KeySpace, models.Collections.OTP)
|
otpIndexQueryPhoneNumber := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_otp_phone_number ON %s.%s (phone_number)", KeySpace, models.Collections.OTP)
|
||||||
err = session.Query(otpIndexQueryPhoneNumber).Exec()
|
err = session.Query(otpIndexQueryPhoneNumber).Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// add authenticators table
|
||||||
|
totpCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, user_id text, method text, secret text, recovery_codes text, verified_at bigint, updated_at bigint, created_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.Authenticators)
|
||||||
|
err = session.Query(totpCollectionQuery).Exec()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &provider{
|
return &provider{
|
||||||
db: session,
|
db: session,
|
||||||
}, err
|
}, err
|
||||||
|
@@ -78,6 +78,7 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
|
|||||||
|
|
||||||
query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.User, fields, values)
|
query := fmt.Sprintf("INSERT INTO %s %s VALUES %s IF NOT EXISTS", KeySpace+"."+models.Collections.User, fields, values)
|
||||||
err = p.db.Query(query).Exec()
|
err = p.db.Query(query).Exec()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
@@ -177,13 +178,17 @@ func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination)
|
|||||||
// there is no offset in cassandra
|
// there is no offset in cassandra
|
||||||
// so we fetch till limit + offset
|
// so we fetch till limit + offset
|
||||||
// and return the results from offset to limit
|
// and return the results from offset to limit
|
||||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User, pagination.Limit+pagination.Offset)
|
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s LIMIT %d", KeySpace+"."+models.Collections.User,
|
||||||
|
pagination.Limit+pagination.Offset)
|
||||||
scanner := p.db.Query(query).Iter().Scanner()
|
scanner := p.db.Query(query).Iter().Scanner()
|
||||||
counter := int64(0)
|
counter := int64(0)
|
||||||
for scanner.Next() {
|
for scanner.Next() {
|
||||||
if counter >= pagination.Offset {
|
if counter >= pagination.Offset {
|
||||||
var user models.User
|
var user models.User
|
||||||
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
|
err := scanner.Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods,
|
||||||
|
&user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber,
|
||||||
|
&user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled,
|
||||||
|
&user.AppData, &user.CreatedAt, &user.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -200,8 +205,8 @@ func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination)
|
|||||||
// GetUserByEmail to get user information from database using email address
|
// GetUserByEmail to get user information from database using email address
|
||||||
func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, email)
|
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, email)
|
||||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
|
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.AppData, &user.CreatedAt, &user.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -211,8 +216,8 @@ func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.Us
|
|||||||
// GetUserByID to get user information from database using user ID
|
// GetUserByID to get user information from database using user ID
|
||||||
func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, error) {
|
func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, id)
|
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s WHERE id = '%s' LIMIT 1", KeySpace+"."+models.Collections.User, id)
|
||||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
|
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.AppData, &user.CreatedAt, &user.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -297,17 +302,15 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByPhoneNumber to get user information from database using phone number
|
// GetUserByPhoneNumber to get user information from database using phone number
|
||||||
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
|
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
|
||||||
var user models.User
|
var user models.User
|
||||||
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE phone_number = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, phoneNumber)
|
query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s WHERE phone_number = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, phoneNumber)
|
||||||
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt)
|
err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.AppData, &user.CreatedAt, &user.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -74,7 +74,6 @@ func (p *provider) ListVerificationRequests(ctx context.Context, pagination *mod
|
|||||||
var verificationRequest models.VerificationRequest
|
var verificationRequest models.VerificationRequest
|
||||||
err := scanner.Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
|
err := scanner.Scan(&verificationRequest.ID, &verificationRequest.Token, &verificationRequest.Identifier, &verificationRequest.ExpiresAt, &verificationRequest.Email, &verificationRequest.Nonce, &verificationRequest.RedirectURI, &verificationRequest.CreatedAt, &verificationRequest.UpdatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("=> getting error here...", err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
|
verificationRequests = append(verificationRequests, verificationRequest.AsAPIVerificationRequest())
|
||||||
|
81
server/db/providers/couchbase/authenticator.go
Normal file
81
server/db/providers/couchbase/authenticator.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package couchbase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/couchbase/gocb/v2"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
||||||
|
if exists != nil {
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if authenticators.ID == "" {
|
||||||
|
authenticators.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
authenticators.Key = authenticators.ID
|
||||||
|
authenticators.CreatedAt = time.Now().Unix()
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
insertOpt := gocb.InsertOptions{
|
||||||
|
Context: ctx,
|
||||||
|
}
|
||||||
|
_, err := p.db.Collection(models.Collections.Authenticators).Insert(authenticators.ID, authenticators, &insertOpt)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
bytes, err := json.Marshal(authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// use decoder instead of json.Unmarshall, because it converts int64 -> float64 after unmarshalling
|
||||||
|
decoder := json.NewDecoder(strings.NewReader(string(bytes)))
|
||||||
|
decoder.UseNumber()
|
||||||
|
authenticator := map[string]interface{}{}
|
||||||
|
err = decoder.Decode(&authenticator)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
updateFields, params := GetSetFields(authenticator)
|
||||||
|
query := fmt.Sprintf("UPDATE %s.%s SET %s WHERE _id = '%s'", p.scopeName, models.Collections.Authenticators, updateFields, authenticators.ID)
|
||||||
|
_, err = p.db.Query(query, &gocb.QueryOptions{
|
||||||
|
Context: ctx,
|
||||||
|
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
||||||
|
NamedParameters: params,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
||||||
|
var authenticators *models.Authenticator
|
||||||
|
query := fmt.Sprintf("SELECT _id, user_id, method, secret, recovery_code, verified_at, created_at, updated_at FROM %s.%s WHERE user_id = $1 AND method = $2 LIMIT 1", p.scopeName, models.Collections.Authenticators)
|
||||||
|
q, err := p.db.Query(query, &gocb.QueryOptions{
|
||||||
|
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
||||||
|
Context: ctx,
|
||||||
|
PositionalParameters: []interface{}{userId, authenticatorType},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
err = q.One(&authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
@@ -43,10 +43,10 @@ func (p *provider) AddUser(ctx context.Context, user *models.User) (*models.User
|
|||||||
// UpdateUser to update user information in database
|
// UpdateUser to update user information in database
|
||||||
func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.User, error) {
|
func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.User, error) {
|
||||||
user.UpdatedAt = time.Now().Unix()
|
user.UpdatedAt = time.Now().Unix()
|
||||||
unsertOpt := gocb.UpsertOptions{
|
upsertOpt := gocb.UpsertOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
}
|
}
|
||||||
_, err := p.db.Collection(models.Collections.User).Upsert(user.ID, user, &unsertOpt)
|
_, err := p.db.Collection(models.Collections.User).Upsert(user.ID, user, &upsertOpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ func (p *provider) DeleteUser(ctx context.Context, user *models.User) error {
|
|||||||
func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination) (*model.Users, error) {
|
func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination) (*model.Users, error) {
|
||||||
users := []*model.User{}
|
users := []*model.User{}
|
||||||
paginationClone := pagination
|
paginationClone := pagination
|
||||||
userQuery := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s ORDER BY id OFFSET $1 LIMIT $2", p.scopeName, models.Collections.User)
|
userQuery := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s.%s ORDER BY id OFFSET $1 LIMIT $2", p.scopeName, models.Collections.User)
|
||||||
queryResult, err := p.db.Query(userQuery, &gocb.QueryOptions{
|
queryResult, err := p.db.Query(userQuery, &gocb.QueryOptions{
|
||||||
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
@@ -103,7 +103,7 @@ func (p *provider) ListUsers(ctx context.Context, pagination *model.Pagination)
|
|||||||
// GetUserByEmail to get user information from database using email address
|
// GetUserByEmail to get user information from database using email address
|
||||||
func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||||
var user *models.User
|
var user *models.User
|
||||||
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s WHERE email = $1 LIMIT 1", p.scopeName, models.Collections.User)
|
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s.%s WHERE email = $1 LIMIT 1", p.scopeName, models.Collections.User)
|
||||||
q, err := p.db.Query(query, &gocb.QueryOptions{
|
q, err := p.db.Query(query, &gocb.QueryOptions{
|
||||||
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
@@ -122,7 +122,7 @@ func (p *provider) GetUserByEmail(ctx context.Context, email string) (*models.Us
|
|||||||
// GetUserByID to get user information from database using user ID
|
// GetUserByID to get user information from database using user ID
|
||||||
func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, error) {
|
func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, error) {
|
||||||
var user *models.User
|
var user *models.User
|
||||||
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s WHERE _id = $1 LIMIT 1", p.scopeName, models.Collections.User)
|
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s.%s WHERE _id = $1 LIMIT 1", p.scopeName, models.Collections.User)
|
||||||
q, err := p.db.Query(query, &gocb.QueryOptions{
|
q, err := p.db.Query(query, &gocb.QueryOptions{
|
||||||
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
@@ -175,7 +175,7 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
|
|||||||
// GetUserByPhoneNumber to get user information from database using phone number
|
// GetUserByPhoneNumber to get user information from database using phone number
|
||||||
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
|
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
|
||||||
var user *models.User
|
var user *models.User
|
||||||
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s.%s WHERE phone_number = $1 LIMIT 1", p.scopeName, models.Collections.User)
|
query := fmt.Sprintf("SELECT _id, email, email_verified_at, `password`, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, app_data, created_at, updated_at FROM %s.%s WHERE phone_number = $1 LIMIT 1", p.scopeName, models.Collections.User)
|
||||||
q, err := p.db.Query(query, &gocb.QueryOptions{
|
q, err := p.db.Query(query, &gocb.QueryOptions{
|
||||||
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
ScanConsistency: gocb.QueryScanConsistencyRequestPlus,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
|
57
server/db/providers/dynamodb/authenticator.go
Normal file
57
server/db/providers/dynamodb/authenticator.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package dynamodb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
||||||
|
if exists != nil {
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
collection := p.db.Table(models.Collections.Authenticators)
|
||||||
|
if authenticators.ID == "" {
|
||||||
|
authenticators.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticators.CreatedAt = time.Now().Unix()
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
err := collection.Put(authenticators).RunWithContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
collection := p.db.Table(models.Collections.Authenticators)
|
||||||
|
if authenticators.ID != "" {
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
err := UpdateByHashKey(collection, "id", authenticators.ID, authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
||||||
|
var authenticators *models.Authenticator
|
||||||
|
collection := p.db.Table(models.Collections.Authenticators)
|
||||||
|
iter := collection.Scan().Filter("'user_id' = ?", userId).Filter("'method' = ?", authenticatorType).Iter()
|
||||||
|
for iter.NextWithContext(ctx, &authenticators) {
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
err := iter.Err()
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
@@ -52,6 +52,7 @@ func NewProvider() (*provider, error) {
|
|||||||
db.CreateTable(models.Collections.VerificationRequest, models.VerificationRequest{}).Wait()
|
db.CreateTable(models.Collections.VerificationRequest, models.VerificationRequest{}).Wait()
|
||||||
db.CreateTable(models.Collections.Webhook, models.Webhook{}).Wait()
|
db.CreateTable(models.Collections.Webhook, models.Webhook{}).Wait()
|
||||||
db.CreateTable(models.Collections.WebhookLog, models.WebhookLog{}).Wait()
|
db.CreateTable(models.Collections.WebhookLog, models.WebhookLog{}).Wait()
|
||||||
|
db.CreateTable(models.Collections.Authenticators, models.Authenticator{}).Wait()
|
||||||
return &provider{
|
return &provider{
|
||||||
db: db,
|
db: db,
|
||||||
}, nil
|
}, nil
|
||||||
|
@@ -53,10 +53,6 @@ func (p *provider) UpdateUser(ctx context.Context, user *models.User) (*models.U
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
@@ -136,7 +132,7 @@ func (p *provider) GetUserByID(ctx context.Context, id string) (*models.User, er
|
|||||||
var user *models.User
|
var user *models.User
|
||||||
err := collection.Get("id", id).OneWithContext(ctx, &user)
|
err := collection.Get("id", id).OneWithContext(ctx, &user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if user.Email == "" {
|
if refs.StringValue(user.Email) == "" {
|
||||||
return user, errors.New("no documets found")
|
return user, errors.New("no documets found")
|
||||||
} else {
|
} else {
|
||||||
return user, nil
|
return user, nil
|
||||||
|
52
server/db/providers/mongodb/authenticator.go
Normal file
52
server/db/providers/mongodb/authenticator.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package mongodb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
||||||
|
if exists != nil {
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if authenticators.ID == "" {
|
||||||
|
authenticators.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
authenticators.CreatedAt = time.Now().Unix()
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
authenticators.Key = authenticators.ID
|
||||||
|
authenticatorsCollection := p.db.Collection(models.Collections.Authenticators, options.Collection())
|
||||||
|
_, err := authenticatorsCollection.InsertOne(ctx, authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
authenticatorsCollection := p.db.Collection(models.Collections.Authenticators, options.Collection())
|
||||||
|
_, err := authenticatorsCollection.UpdateOne(ctx, bson.M{"_id": bson.M{"$eq": authenticators.ID}}, bson.M{"$set": authenticators})
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
||||||
|
var authenticators *models.Authenticator
|
||||||
|
authenticatorsCollection := p.db.Collection(models.Collections.Authenticators, options.Collection())
|
||||||
|
err := authenticatorsCollection.FindOne(ctx, bson.M{"user_id": userId, "method": authenticatorType}).Decode(&authenticators)
|
||||||
|
if err != nil {
|
||||||
|
return authenticators, err
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
@@ -47,8 +47,6 @@ func NewProvider() (*provider, error) {
|
|||||||
Keys: bson.M{"email": 1},
|
Keys: bson.M{"email": 1},
|
||||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||||
},
|
},
|
||||||
}, options.CreateIndexes())
|
|
||||||
userCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
|
||||||
{
|
{
|
||||||
Keys: bson.M{"phone_number": 1},
|
Keys: bson.M{"phone_number": 1},
|
||||||
Options: options.Index().SetUnique(true).SetSparse(true).SetPartialFilterExpression(map[string]interface{}{
|
Options: options.Index().SetUnique(true).SetSparse(true).SetPartialFilterExpression(map[string]interface{}{
|
||||||
@@ -56,7 +54,6 @@ func NewProvider() (*provider, error) {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}, options.CreateIndexes())
|
}, options.CreateIndexes())
|
||||||
|
|
||||||
mongodb.CreateCollection(ctx, models.Collections.VerificationRequest, options.CreateCollection())
|
mongodb.CreateCollection(ctx, models.Collections.VerificationRequest, options.CreateCollection())
|
||||||
verificationRequestCollection := mongodb.Collection(models.Collections.VerificationRequest, options.Collection())
|
verificationRequestCollection := mongodb.Collection(models.Collections.VerificationRequest, options.Collection())
|
||||||
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
verificationRequestCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||||
@@ -125,6 +122,15 @@ func NewProvider() (*provider, error) {
|
|||||||
},
|
},
|
||||||
}, options.CreateIndexes())
|
}, options.CreateIndexes())
|
||||||
|
|
||||||
|
mongodb.CreateCollection(ctx, models.Collections.Authenticators, options.CreateCollection())
|
||||||
|
authenticatorsCollection := mongodb.Collection(models.Collections.Authenticators, options.Collection())
|
||||||
|
authenticatorsCollection.Indexes().CreateMany(ctx, []mongo.IndexModel{
|
||||||
|
{
|
||||||
|
Keys: bson.M{"user_id": 1},
|
||||||
|
Options: options.Index().SetSparse(true),
|
||||||
|
},
|
||||||
|
}, options.CreateIndexes())
|
||||||
|
|
||||||
return &provider{
|
return &provider{
|
||||||
db: mongodb,
|
db: mongodb,
|
||||||
}, nil
|
}, nil
|
||||||
|
34
server/db/providers/provider_template/authenticator.go
Normal file
34
server/db/providers/provider_template/authenticator.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package provider_template
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
||||||
|
if exists != nil {
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if authenticators.ID == "" {
|
||||||
|
authenticators.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
authenticators.CreatedAt = time.Now().Unix()
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
||||||
|
var authenticators *models.Authenticator
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
@@ -26,7 +26,7 @@ type Provider interface {
|
|||||||
// If ids set to nil / empty all the users will be updated
|
// If ids set to nil / empty all the users will be updated
|
||||||
UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error
|
UpdateUsers(ctx context.Context, data map[string]interface{}, ids []string) error
|
||||||
|
|
||||||
// AddVerification to save verification request in database
|
// AddVerificationRequest to save verification request in database
|
||||||
AddVerificationRequest(ctx context.Context, verificationRequest *models.VerificationRequest) (*models.VerificationRequest, error)
|
AddVerificationRequest(ctx context.Context, verificationRequest *models.VerificationRequest) (*models.VerificationRequest, error)
|
||||||
// GetVerificationRequestByToken to get verification request from database using token
|
// GetVerificationRequestByToken to get verification request from database using token
|
||||||
GetVerificationRequestByToken(ctx context.Context, token string) (*models.VerificationRequest, error)
|
GetVerificationRequestByToken(ctx context.Context, token string) (*models.VerificationRequest, error)
|
||||||
@@ -53,7 +53,7 @@ type Provider interface {
|
|||||||
AddWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error)
|
AddWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error)
|
||||||
// UpdateWebhook to update webhook
|
// UpdateWebhook to update webhook
|
||||||
UpdateWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error)
|
UpdateWebhook(ctx context.Context, webhook *models.Webhook) (*model.Webhook, error)
|
||||||
// ListWebhooks to list webhook
|
// ListWebhook to list webhook
|
||||||
ListWebhook(ctx context.Context, pagination *model.Pagination) (*model.Webhooks, error)
|
ListWebhook(ctx context.Context, pagination *model.Pagination) (*model.Webhooks, error)
|
||||||
// GetWebhookByID to get webhook by id
|
// GetWebhookByID to get webhook by id
|
||||||
GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error)
|
GetWebhookByID(ctx context.Context, webhookID string) (*model.Webhook, error)
|
||||||
@@ -71,7 +71,7 @@ type Provider interface {
|
|||||||
AddEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error)
|
AddEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error)
|
||||||
// UpdateEmailTemplate to update EmailTemplate
|
// UpdateEmailTemplate to update EmailTemplate
|
||||||
UpdateEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error)
|
UpdateEmailTemplate(ctx context.Context, emailTemplate *models.EmailTemplate) (*model.EmailTemplate, error)
|
||||||
// ListEmailTemplates to list EmailTemplate
|
// ListEmailTemplate to list EmailTemplate
|
||||||
ListEmailTemplate(ctx context.Context, pagination *model.Pagination) (*model.EmailTemplates, error)
|
ListEmailTemplate(ctx context.Context, pagination *model.Pagination) (*model.EmailTemplates, error)
|
||||||
// GetEmailTemplateByID to get EmailTemplate by id
|
// GetEmailTemplateByID to get EmailTemplate by id
|
||||||
GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error)
|
GetEmailTemplateByID(ctx context.Context, emailTemplateID string) (*model.EmailTemplate, error)
|
||||||
@@ -88,4 +88,15 @@ type Provider interface {
|
|||||||
GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error)
|
GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error)
|
||||||
// DeleteOTP to delete otp
|
// DeleteOTP to delete otp
|
||||||
DeleteOTP(ctx context.Context, otp *models.OTP) error
|
DeleteOTP(ctx context.Context, otp *models.OTP) error
|
||||||
|
|
||||||
|
// AddAuthenticator adds a new authenticator document to the database.
|
||||||
|
// If the authenticator doesn't have an ID, a new one is generated.
|
||||||
|
// The created document is returned, or an error if the operation fails.
|
||||||
|
AddAuthenticator(ctx context.Context, totp *models.Authenticator) (*models.Authenticator, error)
|
||||||
|
// UpdateAuthenticator updates an existing authenticator document in the database.
|
||||||
|
// The updated document is returned, or an error if the operation fails.
|
||||||
|
UpdateAuthenticator(ctx context.Context, totp *models.Authenticator) (*models.Authenticator, error)
|
||||||
|
// GetAuthenticatorDetailsByUserId retrieves details of an authenticator document based on user ID and authenticator type.
|
||||||
|
// If found, the authenticator document is returned, or an error if not found or an error occurs during the retrieval.
|
||||||
|
GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error)
|
||||||
}
|
}
|
||||||
|
52
server/db/providers/sql/authenticator.go
Normal file
52
server/db/providers/sql/authenticator.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *provider) AddAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
exists, _ := p.GetAuthenticatorDetailsByUserId(ctx, authenticators.UserID, authenticators.Method)
|
||||||
|
if exists != nil {
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if authenticators.ID == "" {
|
||||||
|
authenticators.ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
authenticators.Key = authenticators.ID
|
||||||
|
authenticators.CreatedAt = time.Now().Unix()
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
res := p.db.Clauses(
|
||||||
|
clause.OnConflict{
|
||||||
|
UpdateAll: true,
|
||||||
|
Columns: []clause.Column{{Name: "id"}},
|
||||||
|
}).Create(&authenticators)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) UpdateAuthenticator(ctx context.Context, authenticators *models.Authenticator) (*models.Authenticator, error) {
|
||||||
|
authenticators.UpdatedAt = time.Now().Unix()
|
||||||
|
result := p.db.Save(&authenticators)
|
||||||
|
if result.Error != nil {
|
||||||
|
return authenticators, result.Error
|
||||||
|
}
|
||||||
|
return authenticators, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) GetAuthenticatorDetailsByUserId(ctx context.Context, userId string, authenticatorType string) (*models.Authenticator, error) {
|
||||||
|
var authenticators models.Authenticator
|
||||||
|
result := p.db.Where("user_id = ?", userId).Where("method = ?", authenticatorType).First(&authenticators)
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil, result.Error
|
||||||
|
}
|
||||||
|
return &authenticators, nil
|
||||||
|
}
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
|
libsql "github.com/ekristen/gorm-libsql"
|
||||||
"github.com/glebarez/sqlite"
|
"github.com/glebarez/sqlite"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
@@ -60,6 +61,8 @@ func NewProvider() (*provider, error) {
|
|||||||
sqlDB, err = gorm.Open(postgres.Open(dbURL), ormConfig)
|
sqlDB, err = gorm.Open(postgres.Open(dbURL), ormConfig)
|
||||||
case constants.DbTypeSqlite:
|
case constants.DbTypeSqlite:
|
||||||
sqlDB, err = gorm.Open(sqlite.Open(dbURL+"?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)"), ormConfig)
|
sqlDB, err = gorm.Open(sqlite.Open(dbURL+"?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)"), ormConfig)
|
||||||
|
case constants.DbTypeLibSQL:
|
||||||
|
sqlDB, err = gorm.Open(libsql.Open(dbURL), ormConfig)
|
||||||
case constants.DbTypeMysql, constants.DbTypeMariaDB, constants.DbTypePlanetScaleDB:
|
case constants.DbTypeMysql, constants.DbTypeMariaDB, constants.DbTypePlanetScaleDB:
|
||||||
sqlDB, err = gorm.Open(mysql.Open(dbURL), ormConfig)
|
sqlDB, err = gorm.Open(mysql.Open(dbURL), ormConfig)
|
||||||
case constants.DbTypeSqlserver:
|
case constants.DbTypeSqlserver:
|
||||||
@@ -77,7 +80,7 @@ func NewProvider() (*provider, error) {
|
|||||||
logrus.Debug("Failed to drop phone number constraint:", err)
|
logrus.Debug("Failed to drop phone number constraint:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{}, &models.Webhook{}, &models.WebhookLog{}, &models.EmailTemplate{}, &models.OTP{})
|
err = sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{}, &models.Webhook{}, &models.WebhookLog{}, &models.EmailTemplate{}, &models.OTP{}, &models.Authenticator{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
@@ -72,7 +73,6 @@ func getEmailTemplate(event string, data map[string]interface{}) (*model.EmailTe
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
subjectString := buf.String()
|
subjectString := buf.String()
|
||||||
|
|
||||||
return &model.EmailTemplate{
|
return &model.EmailTemplate{
|
||||||
Template: templateString,
|
Template: templateString,
|
||||||
Subject: subjectString,
|
Subject: subjectString,
|
||||||
@@ -92,10 +92,16 @@ func SendEmail(to []string, event string, data map[string]interface{}) error {
|
|||||||
|
|
||||||
tmp, err := getEmailTemplate(event, data)
|
tmp, err := getEmailTemplate(event, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to get event template: ", err)
|
log.Error("Failed to get event template: ", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mailgunAPIKey := os.Getenv("MAILGUN_API_KEY")
|
||||||
|
|
||||||
|
if len(mailgunAPIKey) > 0 {
|
||||||
|
return SendMailgun(to, event, data)
|
||||||
|
}
|
||||||
|
|
||||||
m := gomail.NewMessage()
|
m := gomail.NewMessage()
|
||||||
senderEmail, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySenderEmail)
|
senderEmail, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySenderEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
83
server/email/mailgun.go
Normal file
83
server/email/mailgun.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package email
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mailgun "github.com/mailgun/mailgun-go/v4"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
const apiURL = "https://api.mailgun.net/v3/%s/messages"
|
||||||
|
|
||||||
|
func MailgunRest(to string, data map[string]interface{}, subject string, template string) error {
|
||||||
|
var mailgunAPIKey = os.Getenv("MAILGUN_API_KEY")
|
||||||
|
var mailgunDomain = os.Getenv("MAILGUN_DOMAIN")
|
||||||
|
sender := mailgunDomain + "<noreply@" + mailgunDomain + ">"
|
||||||
|
log.Printf("%r", data)
|
||||||
|
mg := mailgun.NewMailgun(mailgunDomain, mailgunAPIKey)
|
||||||
|
m := mg.NewMessage(sender, subject, "", to)
|
||||||
|
m.SetTemplate(template)
|
||||||
|
m.AddTemplateVariable("verification_url", data["verification_url"])
|
||||||
|
userMap, ok := data["user"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
log.Println("Error: Unable to retrieve user information from the data map.")
|
||||||
|
}
|
||||||
|
userName, ok := userMap["GivenName"].(string)
|
||||||
|
if ok {
|
||||||
|
m.AddTemplateVariable("username", userName)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
resp, id, err := mg.Send(ctx, m)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("ID: %s Resp: %s\n", id, resp)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMailgun function to send
|
||||||
|
func SendMailgun(to []string, event string, data map[string]interface{}) error {
|
||||||
|
template := "authorizer_email_confirmation"
|
||||||
|
|
||||||
|
switch event {
|
||||||
|
case constants.VerificationTypeBasicAuthSignup:
|
||||||
|
template = "authorizer_email_confirmation"
|
||||||
|
case constants.VerificationTypeForgotPassword:
|
||||||
|
template = "authorizer_password_reset"
|
||||||
|
case constants.VerificationTypeInviteMember:
|
||||||
|
template = "author_invited"
|
||||||
|
case constants.VerificationTypeMagicLinkLogin:
|
||||||
|
template = "magic_link_login"
|
||||||
|
case constants.VerificationTypeOTP:
|
||||||
|
template = "one_time_password"
|
||||||
|
case constants.VerificationTypeUpdateEmail:
|
||||||
|
template = "email_update"
|
||||||
|
}
|
||||||
|
|
||||||
|
subject := "Подтверждение почты"
|
||||||
|
|
||||||
|
// TODO: language selection logic here
|
||||||
|
|
||||||
|
err := MailgunRest(to[0], data, subject, template)
|
||||||
|
|
||||||
|
// Log the response
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error sending email: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Println("Email sent successfully")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
53
server/env/env.go
vendored
53
server/env/env.go
vendored
@@ -104,8 +104,12 @@ func InitAllEnv() error {
|
|||||||
osDisableStrongPassword := os.Getenv(constants.EnvKeyDisableStrongPassword)
|
osDisableStrongPassword := os.Getenv(constants.EnvKeyDisableStrongPassword)
|
||||||
osEnforceMultiFactorAuthentication := os.Getenv(constants.EnvKeyEnforceMultiFactorAuthentication)
|
osEnforceMultiFactorAuthentication := os.Getenv(constants.EnvKeyEnforceMultiFactorAuthentication)
|
||||||
osDisableMultiFactorAuthentication := os.Getenv(constants.EnvKeyDisableMultiFactorAuthentication)
|
osDisableMultiFactorAuthentication := os.Getenv(constants.EnvKeyDisableMultiFactorAuthentication)
|
||||||
|
osDisableTOTPLogin := os.Getenv(constants.EnvKeyDisableTOTPLogin)
|
||||||
|
osDisableMailOTPLogin := os.Getenv(constants.EnvKeyDisableMailOTPLogin)
|
||||||
// phone verification var
|
// phone verification var
|
||||||
osDisablePhoneVerification := os.Getenv(constants.EnvKeyDisablePhoneVerification)
|
osDisablePhoneVerification := os.Getenv(constants.EnvKeyDisablePhoneVerification)
|
||||||
|
osDisablePlayground := os.Getenv(constants.EnvKeyDisablePlayGround)
|
||||||
|
|
||||||
// twilio vars
|
// twilio vars
|
||||||
osTwilioApiKey := os.Getenv(constants.EnvKeyTwilioAPIKey)
|
osTwilioApiKey := os.Getenv(constants.EnvKeyTwilioAPIKey)
|
||||||
osTwilioApiSecret := os.Getenv(constants.EnvKeyTwilioAPISecret)
|
osTwilioApiSecret := os.Getenv(constants.EnvKeyTwilioAPISecret)
|
||||||
@@ -687,20 +691,13 @@ func InitAllEnv() error {
|
|||||||
envData[constants.EnvKeyDisableEmailVerification] = true
|
envData[constants.EnvKeyDisableEmailVerification] = true
|
||||||
envData[constants.EnvKeyDisableMagicLinkLogin] = true
|
envData[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||||
envData[constants.EnvKeyIsEmailServiceEnabled] = false
|
envData[constants.EnvKeyIsEmailServiceEnabled] = false
|
||||||
|
envData[constants.EnvKeyDisableMailOTPLogin] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData[constants.EnvKeySmtpHost] != "" && envData[constants.EnvKeySmtpUsername] != "" && envData[constants.EnvKeySmtpPassword] != "" && envData[constants.EnvKeySenderEmail] != "" && envData[constants.EnvKeySmtpPort] != "" {
|
if envData[constants.EnvKeySmtpHost] != "" && envData[constants.EnvKeySmtpUsername] != "" && envData[constants.EnvKeySmtpPassword] != "" && envData[constants.EnvKeySenderEmail] != "" && envData[constants.EnvKeySmtpPort] != "" {
|
||||||
envData[constants.EnvKeyIsEmailServiceEnabled] = true
|
envData[constants.EnvKeyIsEmailServiceEnabled] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if envData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && !envData[constants.EnvKeyIsEmailServiceEnabled].(bool) && !envData[constants.EnvKeyIsSMSServiceEnabled].(bool) {
|
|
||||||
return errors.New("to enable multi factor authentication, please enable email service")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !envData[constants.EnvKeyIsEmailServiceEnabled].(bool) {
|
|
||||||
envData[constants.EnvKeyDisableMultiFactorAuthentication] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if envData[constants.EnvKeyDisableEmailVerification].(bool) {
|
if envData[constants.EnvKeyDisableEmailVerification].(bool) {
|
||||||
envData[constants.EnvKeyDisableMagicLinkLogin] = true
|
envData[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||||
}
|
}
|
||||||
@@ -825,6 +822,46 @@ func InitAllEnv() error {
|
|||||||
envData[constants.EnvKeyIsSMSServiceEnabled] = true
|
envData[constants.EnvKeyIsSMSServiceEnabled] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := envData[constants.EnvKeyDisablePlayGround]; !ok {
|
||||||
|
envData[constants.EnvKeyDisablePlayGround] = osDisablePlayground == "true"
|
||||||
|
}
|
||||||
|
if osDisablePlayground != "" {
|
||||||
|
boolValue, err := strconv.ParseBool(osDisablePlayground)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if boolValue != envData[constants.EnvKeyDisablePlayGround].(bool) {
|
||||||
|
envData[constants.EnvKeyDisablePlayGround] = boolValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: remove after beta launch
|
||||||
|
envData[constants.EnvKeyDisableTOTPLogin] = true
|
||||||
|
if _, ok := envData[constants.EnvKeyDisableTOTPLogin]; !ok {
|
||||||
|
envData[constants.EnvKeyDisableTOTPLogin] = osDisableTOTPLogin == "true"
|
||||||
|
}
|
||||||
|
if osDisableTOTPLogin != "" {
|
||||||
|
boolValue, err := strconv.ParseBool(osDisableTOTPLogin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if boolValue != envData[constants.EnvKeyDisableTOTPLogin].(bool) {
|
||||||
|
envData[constants.EnvKeyDisableTOTPLogin] = boolValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := envData[constants.EnvKeyDisableMailOTPLogin]; !ok {
|
||||||
|
envData[constants.EnvKeyDisableMailOTPLogin] = osDisableMailOTPLogin == "true"
|
||||||
|
}
|
||||||
|
if osDisableMailOTPLogin != "" {
|
||||||
|
boolValue, err := strconv.ParseBool(osDisableMailOTPLogin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if boolValue != envData[constants.EnvKeyDisableMailOTPLogin].(bool) {
|
||||||
|
envData[constants.EnvKeyDisableMailOTPLogin] = boolValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = memorystore.Provider.UpdateEnvStore(envData)
|
err = memorystore.Provider.UpdateEnvStore(envData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error while updating env store: ", err)
|
log.Debug("Error while updating env store: ", err)
|
||||||
|
11
server/env/persist_env.go
vendored
11
server/env/persist_env.go
vendored
@@ -196,7 +196,7 @@ func PersistEnv() error {
|
|||||||
envValue := strings.TrimSpace(os.Getenv(key))
|
envValue := strings.TrimSpace(os.Getenv(key))
|
||||||
if envValue != "" {
|
if envValue != "" {
|
||||||
switch key {
|
switch key {
|
||||||
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyIsSMSServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure, constants.EnvKeyDisablePhoneVerification:
|
case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyIsSMSServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure, constants.EnvKeyDisablePhoneVerification, constants.EnvKeyDisablePlayGround, constants.EnvKeyDisableTOTPLogin, constants.EnvKeyDisableMailOTPLogin:
|
||||||
if envValueBool, err := strconv.ParseBool(envValue); err == nil {
|
if envValueBool, err := strconv.ParseBool(envValue); err == nil {
|
||||||
if value.(bool) != envValueBool {
|
if value.(bool) != envValueBool {
|
||||||
storeData[key] = envValueBool
|
storeData[key] = envValueBool
|
||||||
@@ -218,15 +218,20 @@ func PersistEnv() error {
|
|||||||
if storeData[constants.EnvKeySmtpHost] == "" || storeData[constants.EnvKeySmtpUsername] == "" || storeData[constants.EnvKeySmtpPassword] == "" || storeData[constants.EnvKeySenderEmail] == "" && storeData[constants.EnvKeySmtpPort] == "" {
|
if storeData[constants.EnvKeySmtpHost] == "" || storeData[constants.EnvKeySmtpUsername] == "" || storeData[constants.EnvKeySmtpPassword] == "" || storeData[constants.EnvKeySenderEmail] == "" && storeData[constants.EnvKeySmtpPort] == "" {
|
||||||
storeData[constants.EnvKeyIsEmailServiceEnabled] = false
|
storeData[constants.EnvKeyIsEmailServiceEnabled] = false
|
||||||
|
|
||||||
if !storeData[constants.EnvKeyDisableEmailVerification].(bool) {
|
if val, ok := storeData[constants.EnvKeyDisableEmailVerification]; ok && val != nil && !val.(bool) {
|
||||||
storeData[constants.EnvKeyDisableEmailVerification] = true
|
storeData[constants.EnvKeyDisableEmailVerification] = true
|
||||||
hasChanged = true
|
hasChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !storeData[constants.EnvKeyDisableMagicLinkLogin].(bool) {
|
if val, ok := storeData[constants.EnvKeyDisableMagicLinkLogin]; ok && val != nil && !val.(bool) {
|
||||||
storeData[constants.EnvKeyDisableMagicLinkLogin] = true
|
storeData[constants.EnvKeyDisableMagicLinkLogin] = true
|
||||||
hasChanged = true
|
hasChanged = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := storeData[constants.EnvKeyDisableMailOTPLogin]; ok && val != nil && !val.(bool) {
|
||||||
|
storeData[constants.EnvKeyDisableMailOTPLogin] = true
|
||||||
|
hasChanged = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = memorystore.Provider.UpdateEnvStore(storeData)
|
err = memorystore.Provider.UpdateEnvStore(storeData)
|
||||||
|
154
server/go.mod
154
server/go.mod
@@ -1,42 +1,128 @@
|
|||||||
module github.com/authorizerdev/authorizer/server
|
module github.com/authorizerdev/authorizer/server
|
||||||
|
|
||||||
go 1.16
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.21.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/99designs/gqlgen v0.17.20
|
github.com/99designs/gqlgen v0.17.39
|
||||||
github.com/arangodb/go-driver v1.2.1
|
github.com/arangodb/go-driver v1.6.0
|
||||||
github.com/aws/aws-sdk-go v1.44.298
|
github.com/aws/aws-sdk-go v1.47.4
|
||||||
github.com/coreos/go-oidc/v3 v3.1.0
|
github.com/coreos/go-oidc/v3 v3.6.0
|
||||||
github.com/couchbase/gocb/v2 v2.6.0
|
github.com/couchbase/gocb/v2 v2.6.4
|
||||||
github.com/gin-gonic/gin v1.8.1
|
github.com/ekristen/gorm-libsql v0.0.0-20231101204708-6e113112bcc2
|
||||||
github.com/glebarez/sqlite v1.5.0
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
github.com/glebarez/sqlite v1.10.0
|
||||||
github.com/goccy/go-json v0.9.11 // indirect
|
github.com/gocql/gocql v1.6.0
|
||||||
github.com/gocql/gocql v1.2.0
|
github.com/gokyle/twofactor v1.0.1
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/google/uuid v1.3.1
|
||||||
github.com/google/go-cmp v0.5.6 // indirect
|
github.com/guregu/dynamo v1.20.2
|
||||||
github.com/google/uuid v1.3.0
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/guregu/dynamo v1.20.0
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/redis/go-redis/v9 v9.2.1
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/robertkrimen/otto v0.2.1
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/redis/go-redis/v9 v9.0.3
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
|
github.com/tuotoo/qrcode v0.0.0-20220425170535-52ccc2bebf5d
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/twilio/twilio-go v1.14.1
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/vektah/gqlparser/v2 v2.5.10
|
||||||
github.com/twilio/twilio-go v1.7.2
|
go.mongodb.org/mongo-driver v1.12.1
|
||||||
github.com/vektah/gqlparser/v2 v2.5.1
|
golang.org/x/crypto v0.14.0
|
||||||
go.mongodb.org/mongo-driver v1.8.1
|
golang.org/x/oauth2 v0.13.0
|
||||||
golang.org/x/crypto v0.4.0
|
google.golang.org/appengine v1.6.8
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
|
||||||
gopkg.in/mail.v2 v2.3.1
|
gopkg.in/mail.v2 v2.3.1
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
gorm.io/driver/mysql v1.4.3
|
gorm.io/driver/mysql v1.5.2
|
||||||
gorm.io/driver/postgres v1.4.7
|
gorm.io/driver/postgres v1.5.4
|
||||||
gorm.io/driver/sqlserver v1.4.1
|
gorm.io/driver/sqlserver v1.5.2
|
||||||
gorm.io/gorm v1.24.2
|
gorm.io/gorm v1.25.5
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||||
|
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 // indirect
|
||||||
|
github.com/arangodb/go-velocypack v0.0.0-20200318135517-5af53c29c67e // indirect
|
||||||
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
|
github.com/couchbase/gocbcore/v10 v10.2.8 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||||
|
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||||
|
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||||
|
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||||
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
|
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.4.3 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/klauspost/compress v1.15.15 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
|
github.com/libsql/libsql-client-go v0.0.0-20231026052543-fce76c0f39a7 // indirect
|
||||||
|
github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 // indirect
|
||||||
|
github.com/mailgun/mailgun-go/v4 v4.12.0 // indirect
|
||||||
|
github.com/maruel/rs v1.1.0 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/microsoft/go-mssqldb v1.6.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/montanaflynn/stats v0.7.0 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/sosodev/duration v1.1.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
|
github.com/urfave/cli/v2 v2.25.5 // indirect
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||||
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||||
|
golang.org/x/mod v0.10.0 // indirect
|
||||||
|
golang.org/x/net v0.17.0 // indirect
|
||||||
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
|
golang.org/x/text v0.13.0 // indirect
|
||||||
|
golang.org/x/tools v0.9.3 // indirect
|
||||||
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
modernc.org/libc v1.22.5 // indirect
|
||||||
|
modernc.org/mathutil v1.5.0 // indirect
|
||||||
|
modernc.org/memory v1.5.0 // indirect
|
||||||
|
modernc.org/sqlite v1.23.1 // indirect
|
||||||
|
nhooyr.io/websocket v1.8.7 // indirect
|
||||||
|
rsc.io/qr v0.2.0 // indirect
|
||||||
)
|
)
|
||||||
|
822
server/go.sum
822
server/go.sum
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,15 +6,15 @@ 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"`
|
Design *string `json:"design,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddWebhookRequest struct {
|
type AddWebhookRequest struct {
|
||||||
EventName string `json:"event_name"`
|
EventName string `json:"event_name"`
|
||||||
EventDescription *string `json:"event_description"`
|
EventDescription *string `json:"event_description,omitempty"`
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
Headers map[string]interface{} `json:"headers"`
|
Headers map[string]interface{} `json:"headers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdminLoginInput struct {
|
type AdminLoginInput struct {
|
||||||
@@ -26,14 +26,18 @@ type AdminSignupInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthResponse struct {
|
type AuthResponse struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
ShouldShowEmailOtpScreen *bool `json:"should_show_email_otp_screen"`
|
ShouldShowEmailOtpScreen *bool `json:"should_show_email_otp_screen,omitempty"`
|
||||||
ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_otp_screen"`
|
ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_otp_screen,omitempty"`
|
||||||
AccessToken *string `json:"access_token"`
|
ShouldShowTotpScreen *bool `json:"should_show_totp_screen,omitempty"`
|
||||||
IDToken *string `json:"id_token"`
|
AccessToken *string `json:"access_token,omitempty"`
|
||||||
RefreshToken *string `json:"refresh_token"`
|
IDToken *string `json:"id_token,omitempty"`
|
||||||
ExpiresIn *int64 `json:"expires_in"`
|
RefreshToken *string `json:"refresh_token,omitempty"`
|
||||||
User *User `json:"user"`
|
ExpiresIn *int64 `json:"expires_in,omitempty"`
|
||||||
|
User *User `json:"user,omitempty"`
|
||||||
|
AuthenticatorScannerImage *string `json:"authenticator_scanner_image,omitempty"`
|
||||||
|
AuthenticatorSecret *string `json:"authenticator_secret,omitempty"`
|
||||||
|
AuthenticatorRecoveryCodes []*string `json:"authenticator_recovery_codes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeleteEmailTemplateRequest struct {
|
type DeleteEmailTemplateRequest struct {
|
||||||
@@ -50,8 +54,8 @@ type EmailTemplate struct {
|
|||||||
Template string `json:"template"`
|
Template string `json:"template"`
|
||||||
Design string `json:"design"`
|
Design string `json:"design"`
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at,omitempty"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmailTemplates struct {
|
type EmailTemplates struct {
|
||||||
@@ -60,33 +64,33 @@ type EmailTemplates struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Env struct {
|
type Env struct {
|
||||||
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
|
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME,omitempty"`
|
||||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
AdminSecret *string `json:"ADMIN_SECRET,omitempty"`
|
||||||
DatabaseName *string `json:"DATABASE_NAME"`
|
DatabaseName *string `json:"DATABASE_NAME,omitempty"`
|
||||||
DatabaseURL *string `json:"DATABASE_URL"`
|
DatabaseURL *string `json:"DATABASE_URL,omitempty"`
|
||||||
DatabaseType *string `json:"DATABASE_TYPE"`
|
DatabaseType *string `json:"DATABASE_TYPE,omitempty"`
|
||||||
DatabaseUsername *string `json:"DATABASE_USERNAME"`
|
DatabaseUsername *string `json:"DATABASE_USERNAME,omitempty"`
|
||||||
DatabasePassword *string `json:"DATABASE_PASSWORD"`
|
DatabasePassword *string `json:"DATABASE_PASSWORD,omitempty"`
|
||||||
DatabaseHost *string `json:"DATABASE_HOST"`
|
DatabaseHost *string `json:"DATABASE_HOST,omitempty"`
|
||||||
DatabasePort *string `json:"DATABASE_PORT"`
|
DatabasePort *string `json:"DATABASE_PORT,omitempty"`
|
||||||
ClientID string `json:"CLIENT_ID"`
|
ClientID string `json:"CLIENT_ID"`
|
||||||
ClientSecret string `json:"CLIENT_SECRET"`
|
ClientSecret string `json:"CLIENT_SECRET"`
|
||||||
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT,omitempty"`
|
||||||
SMTPHost *string `json:"SMTP_HOST"`
|
SMTPHost *string `json:"SMTP_HOST,omitempty"`
|
||||||
SMTPPort *string `json:"SMTP_PORT"`
|
SMTPPort *string `json:"SMTP_PORT,omitempty"`
|
||||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
SMTPUsername *string `json:"SMTP_USERNAME,omitempty"`
|
||||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
SMTPPassword *string `json:"SMTP_PASSWORD,omitempty"`
|
||||||
SMTPLocalName *string `json:"SMTP_LOCAL_NAME"`
|
SMTPLocalName *string `json:"SMTP_LOCAL_NAME,omitempty"`
|
||||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
SenderEmail *string `json:"SENDER_EMAIL,omitempty"`
|
||||||
SenderName *string `json:"SENDER_NAME"`
|
SenderName *string `json:"SENDER_NAME,omitempty"`
|
||||||
JwtType *string `json:"JWT_TYPE"`
|
JwtType *string `json:"JWT_TYPE,omitempty"`
|
||||||
JwtSecret *string `json:"JWT_SECRET"`
|
JwtSecret *string `json:"JWT_SECRET,omitempty"`
|
||||||
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY"`
|
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY,omitempty"`
|
||||||
JwtPublicKey *string `json:"JWT_PUBLIC_KEY"`
|
JwtPublicKey *string `json:"JWT_PUBLIC_KEY,omitempty"`
|
||||||
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
AllowedOrigins []string `json:"ALLOWED_ORIGINS,omitempty"`
|
||||||
AppURL *string `json:"APP_URL"`
|
AppURL *string `json:"APP_URL,omitempty"`
|
||||||
RedisURL *string `json:"REDIS_URL"`
|
RedisURL *string `json:"REDIS_URL,omitempty"`
|
||||||
ResetPasswordURL *string `json:"RESET_PASSWORD_URL"`
|
ResetPasswordURL *string `json:"RESET_PASSWORD_URL,omitempty"`
|
||||||
DisableEmailVerification bool `json:"DISABLE_EMAIL_VERIFICATION"`
|
DisableEmailVerification bool `json:"DISABLE_EMAIL_VERIFICATION"`
|
||||||
DisableBasicAuthentication bool `json:"DISABLE_BASIC_AUTHENTICATION"`
|
DisableBasicAuthentication bool `json:"DISABLE_BASIC_AUTHENTICATION"`
|
||||||
DisableMagicLinkLogin bool `json:"DISABLE_MAGIC_LINK_LOGIN"`
|
DisableMagicLinkLogin bool `json:"DISABLE_MAGIC_LINK_LOGIN"`
|
||||||
@@ -96,31 +100,38 @@ type Env struct {
|
|||||||
DisableStrongPassword bool `json:"DISABLE_STRONG_PASSWORD"`
|
DisableStrongPassword bool `json:"DISABLE_STRONG_PASSWORD"`
|
||||||
DisableMultiFactorAuthentication bool `json:"DISABLE_MULTI_FACTOR_AUTHENTICATION"`
|
DisableMultiFactorAuthentication bool `json:"DISABLE_MULTI_FACTOR_AUTHENTICATION"`
|
||||||
EnforceMultiFactorAuthentication bool `json:"ENFORCE_MULTI_FACTOR_AUTHENTICATION"`
|
EnforceMultiFactorAuthentication bool `json:"ENFORCE_MULTI_FACTOR_AUTHENTICATION"`
|
||||||
Roles []string `json:"ROLES"`
|
Roles []string `json:"ROLES,omitempty"`
|
||||||
ProtectedRoles []string `json:"PROTECTED_ROLES"`
|
ProtectedRoles []string `json:"PROTECTED_ROLES,omitempty"`
|
||||||
DefaultRoles []string `json:"DEFAULT_ROLES"`
|
DefaultRoles []string `json:"DEFAULT_ROLES,omitempty"`
|
||||||
JwtRoleClaim *string `json:"JWT_ROLE_CLAIM"`
|
JwtRoleClaim *string `json:"JWT_ROLE_CLAIM,omitempty"`
|
||||||
GoogleClientID *string `json:"GOOGLE_CLIENT_ID"`
|
GoogleClientID *string `json:"GOOGLE_CLIENT_ID,omitempty"`
|
||||||
GoogleClientSecret *string `json:"GOOGLE_CLIENT_SECRET"`
|
GoogleClientSecret *string `json:"GOOGLE_CLIENT_SECRET,omitempty"`
|
||||||
GithubClientID *string `json:"GITHUB_CLIENT_ID"`
|
GithubClientID *string `json:"GITHUB_CLIENT_ID,omitempty"`
|
||||||
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"`
|
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET,omitempty"`
|
||||||
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"`
|
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID,omitempty"`
|
||||||
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"`
|
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET,omitempty"`
|
||||||
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID"`
|
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID,omitempty"`
|
||||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET,omitempty"`
|
||||||
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
AppleClientID *string `json:"APPLE_CLIENT_ID,omitempty"`
|
||||||
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET,omitempty"`
|
||||||
TwitterClientID *string `json:"TWITTER_CLIENT_ID"`
|
DiscordClientID *string `json:"DISCORD_CLIENT_ID,omitempty"`
|
||||||
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET"`
|
DiscordClientSecret *string `json:"DISCORD_CLIENT_SECRET,omitempty"`
|
||||||
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID"`
|
TwitterClientID *string `json:"TWITTER_CLIENT_ID,omitempty"`
|
||||||
MicrosoftClientSecret *string `json:"MICROSOFT_CLIENT_SECRET"`
|
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET,omitempty"`
|
||||||
MicrosoftActiveDirectoryTenantID *string `json:"MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"`
|
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID,omitempty"`
|
||||||
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
MicrosoftClientSecret *string `json:"MICROSOFT_CLIENT_SECRET,omitempty"`
|
||||||
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
MicrosoftActiveDirectoryTenantID *string `json:"MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID,omitempty"`
|
||||||
|
TwitchClientID *string `json:"TWITCH_CLIENT_ID,omitempty"`
|
||||||
|
TwitchClientSecret *string `json:"TWITCH_CLIENT_SECRET,omitempty"`
|
||||||
|
OrganizationName *string `json:"ORGANIZATION_NAME,omitempty"`
|
||||||
|
OrganizationLogo *string `json:"ORGANIZATION_LOGO,omitempty"`
|
||||||
AppCookieSecure bool `json:"APP_COOKIE_SECURE"`
|
AppCookieSecure bool `json:"APP_COOKIE_SECURE"`
|
||||||
AdminCookieSecure bool `json:"ADMIN_COOKIE_SECURE"`
|
AdminCookieSecure bool `json:"ADMIN_COOKIE_SECURE"`
|
||||||
DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE"`
|
DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE,omitempty"`
|
||||||
DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE"`
|
DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE,omitempty"`
|
||||||
|
DisablePlayground bool `json:"DISABLE_PLAYGROUND"`
|
||||||
|
DisableMailOtpLogin bool `json:"DISABLE_MAIL_OTP_LOGIN"`
|
||||||
|
DisableTotpLogin bool `json:"DISABLE_TOTP_LOGIN"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
@@ -129,9 +140,15 @@ type Error struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ForgotPasswordInput struct {
|
type ForgotPasswordInput struct {
|
||||||
Email string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
State *string `json:"state"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
State *string `json:"state,omitempty"`
|
||||||
|
RedirectURI *string `json:"redirect_uri,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ForgotPasswordResponse struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_otp_screen,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GenerateJWTKeysInput struct {
|
type GenerateJWTKeysInput struct {
|
||||||
@@ -139,19 +156,19 @@ type GenerateJWTKeysInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GenerateJWTKeysResponse struct {
|
type GenerateJWTKeysResponse struct {
|
||||||
Secret *string `json:"secret"`
|
Secret *string `json:"secret,omitempty"`
|
||||||
PublicKey *string `json:"public_key"`
|
PublicKey *string `json:"public_key,omitempty"`
|
||||||
PrivateKey *string `json:"private_key"`
|
PrivateKey *string `json:"private_key,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetUserRequest struct {
|
type GetUserRequest struct {
|
||||||
ID *string `json:"id"`
|
ID *string `json:"id,omitempty"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteMemberInput struct {
|
type InviteMemberInput struct {
|
||||||
Emails []string `json:"emails"`
|
Emails []string `json:"emails"`
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
RedirectURI *string `json:"redirect_uri,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteMembersResponse struct {
|
type InviteMembersResponse struct {
|
||||||
@@ -160,69 +177,75 @@ type InviteMembersResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ListWebhookLogRequest struct {
|
type ListWebhookLogRequest struct {
|
||||||
Pagination *PaginationInput `json:"pagination"`
|
Pagination *PaginationInput `json:"pagination,omitempty"`
|
||||||
WebhookID *string `json:"webhook_id"`
|
WebhookID *string `json:"webhook_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginInput struct {
|
type LoginInput struct {
|
||||||
Email string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
Password string `json:"password"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
Roles []string `json:"roles"`
|
Password string `json:"password"`
|
||||||
Scope []string `json:"scope"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
State *string `json:"state"`
|
Scope []string `json:"scope,omitempty"`
|
||||||
|
State *string `json:"state,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MagicLinkLoginInput struct {
|
type MagicLinkLoginInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
Scope []string `json:"scope"`
|
Scope []string `json:"scope,omitempty"`
|
||||||
State *string `json:"state"`
|
State *string `json:"state,omitempty"`
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
RedirectURI *string `json:"redirect_uri,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
ClientID string `json:"client_id"`
|
ClientID string `json:"client_id"`
|
||||||
IsGoogleLoginEnabled bool `json:"is_google_login_enabled"`
|
IsGoogleLoginEnabled bool `json:"is_google_login_enabled"`
|
||||||
IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"`
|
IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"`
|
||||||
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"`
|
IsDiscordLoginEnabled bool `json:"is_discord_login_enabled"`
|
||||||
IsMicrosoftLoginEnabled bool `json:"is_microsoft_login_enabled"`
|
IsTwitterLoginEnabled bool `json:"is_twitter_login_enabled"`
|
||||||
IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"`
|
IsMicrosoftLoginEnabled bool `json:"is_microsoft_login_enabled"`
|
||||||
IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"`
|
IsTwitchLoginEnabled bool `json:"is_twitch_login_enabled"`
|
||||||
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"`
|
||||||
IsSignUpEnabled bool `json:"is_sign_up_enabled"`
|
IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"`
|
||||||
IsStrongPasswordEnabled bool `json:"is_strong_password_enabled"`
|
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
||||||
IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"`
|
IsSignUpEnabled bool `json:"is_sign_up_enabled"`
|
||||||
|
IsStrongPasswordEnabled bool `json:"is_strong_password_enabled"`
|
||||||
|
IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"`
|
||||||
|
IsMobileBasicAuthenticationEnabled bool `json:"is_mobile_basic_authentication_enabled"`
|
||||||
|
IsPhoneVerificationEnabled bool `json:"is_phone_verification_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MobileLoginInput struct {
|
type MobileLoginInput struct {
|
||||||
PhoneNumber string `json:"phone_number"`
|
PhoneNumber string `json:"phone_number"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
Scope []string `json:"scope"`
|
Scope []string `json:"scope,omitempty"`
|
||||||
State *string `json:"state"`
|
State *string `json:"state,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MobileSignUpInput struct {
|
type MobileSignUpInput struct {
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
GivenName *string `json:"given_name"`
|
GivenName *string `json:"given_name,omitempty"`
|
||||||
FamilyName *string `json:"family_name"`
|
FamilyName *string `json:"family_name,omitempty"`
|
||||||
MiddleName *string `json:"middle_name"`
|
MiddleName *string `json:"middle_name,omitempty"`
|
||||||
Nickname *string `json:"nickname"`
|
Nickname *string `json:"nickname,omitempty"`
|
||||||
Gender *string `json:"gender"`
|
Gender *string `json:"gender,omitempty"`
|
||||||
Birthdate *string `json:"birthdate"`
|
Birthdate *string `json:"birthdate,omitempty"`
|
||||||
PhoneNumber string `json:"phone_number"`
|
PhoneNumber string `json:"phone_number"`
|
||||||
Picture *string `json:"picture"`
|
Picture *string `json:"picture,omitempty"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
ConfirmPassword string `json:"confirm_password"`
|
ConfirmPassword string `json:"confirm_password"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
Scope []string `json:"scope"`
|
Scope []string `json:"scope,omitempty"`
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
RedirectURI *string `json:"redirect_uri,omitempty"`
|
||||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled,omitempty"`
|
||||||
State *string `json:"state"`
|
State *string `json:"state,omitempty"`
|
||||||
|
AppData map[string]interface{} `json:"app_data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OAuthRevokeInput struct {
|
type OAuthRevokeInput struct {
|
||||||
@@ -230,7 +253,7 @@ type OAuthRevokeInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PaginatedInput struct {
|
type PaginatedInput struct {
|
||||||
Pagination *PaginationInput `json:"pagination"`
|
Pagination *PaginationInput `json:"pagination,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pagination struct {
|
type Pagination struct {
|
||||||
@@ -241,26 +264,28 @@ type Pagination struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PaginationInput struct {
|
type PaginationInput struct {
|
||||||
Limit *int64 `json:"limit"`
|
Limit *int64 `json:"limit,omitempty"`
|
||||||
Page *int64 `json:"page"`
|
Page *int64 `json:"page,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResendOTPRequest struct {
|
type ResendOTPRequest struct {
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
PhoneNumber *string `json:"phone_number"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
State *string `json:"state"`
|
State *string `json:"state,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResendVerifyEmailInput struct {
|
type ResendVerifyEmailInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Identifier string `json:"identifier"`
|
Identifier string `json:"identifier"`
|
||||||
State *string `json:"state"`
|
State *string `json:"state,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResetPasswordInput struct {
|
type ResetPasswordInput struct {
|
||||||
Token string `json:"token"`
|
Token *string `json:"token,omitempty"`
|
||||||
Password string `json:"password"`
|
Otp *string `json:"otp,omitempty"`
|
||||||
ConfirmPassword string `json:"confirm_password"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
ConfirmPassword string `json:"confirm_password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
@@ -273,42 +298,44 @@ type SMSVerificationRequests struct {
|
|||||||
CodeExpiresAt int64 `json:"code_expires_at"`
|
CodeExpiresAt int64 `json:"code_expires_at"`
|
||||||
PhoneNumber string `json:"phone_number"`
|
PhoneNumber string `json:"phone_number"`
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionQueryInput struct {
|
type SessionQueryInput struct {
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
Scope []string `json:"scope"`
|
Scope []string `json:"scope,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignUpInput struct {
|
type SignUpInput struct {
|
||||||
Email string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
GivenName *string `json:"given_name"`
|
GivenName *string `json:"given_name,omitempty"`
|
||||||
FamilyName *string `json:"family_name"`
|
FamilyName *string `json:"family_name,omitempty"`
|
||||||
MiddleName *string `json:"middle_name"`
|
MiddleName *string `json:"middle_name,omitempty"`
|
||||||
Nickname *string `json:"nickname"`
|
Nickname *string `json:"nickname,omitempty"`
|
||||||
Gender *string `json:"gender"`
|
Gender *string `json:"gender,omitempty"`
|
||||||
Birthdate *string `json:"birthdate"`
|
Birthdate *string `json:"birthdate,omitempty"`
|
||||||
PhoneNumber *string `json:"phone_number"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
Picture *string `json:"picture"`
|
Picture *string `json:"picture,omitempty"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
ConfirmPassword string `json:"confirm_password"`
|
ConfirmPassword string `json:"confirm_password"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
Scope []string `json:"scope"`
|
Scope []string `json:"scope,omitempty"`
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
RedirectURI *string `json:"redirect_uri,omitempty"`
|
||||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled,omitempty"`
|
||||||
State *string `json:"state"`
|
State *string `json:"state,omitempty"`
|
||||||
|
AppData map[string]interface{} `json:"app_data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestEndpointRequest struct {
|
type TestEndpointRequest struct {
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
EventName string `json:"event_name"`
|
EventName string `json:"event_name"`
|
||||||
Headers map[string]interface{} `json:"headers"`
|
EventDescription *string `json:"event_description,omitempty"`
|
||||||
|
Headers map[string]interface{} `json:"headers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TestEndpointResponse struct {
|
type TestEndpointResponse struct {
|
||||||
HTTPStatus *int64 `json:"http_status"`
|
HTTPStatus *int64 `json:"http_status,omitempty"`
|
||||||
Response *string `json:"response"`
|
Response *string `json:"response,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateAccessInput struct {
|
type UpdateAccessInput struct {
|
||||||
@@ -317,128 +344,138 @@ type UpdateAccessInput struct {
|
|||||||
|
|
||||||
type UpdateEmailTemplateRequest struct {
|
type UpdateEmailTemplateRequest struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
EventName *string `json:"event_name"`
|
EventName *string `json:"event_name,omitempty"`
|
||||||
Template *string `json:"template"`
|
Template *string `json:"template,omitempty"`
|
||||||
Subject *string `json:"subject"`
|
Subject *string `json:"subject,omitempty"`
|
||||||
Design *string `json:"design"`
|
Design *string `json:"design,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateEnvInput struct {
|
type UpdateEnvInput struct {
|
||||||
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME"`
|
AccessTokenExpiryTime *string `json:"ACCESS_TOKEN_EXPIRY_TIME,omitempty"`
|
||||||
AdminSecret *string `json:"ADMIN_SECRET"`
|
AdminSecret *string `json:"ADMIN_SECRET,omitempty"`
|
||||||
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT"`
|
CustomAccessTokenScript *string `json:"CUSTOM_ACCESS_TOKEN_SCRIPT,omitempty"`
|
||||||
OldAdminSecret *string `json:"OLD_ADMIN_SECRET"`
|
OldAdminSecret *string `json:"OLD_ADMIN_SECRET,omitempty"`
|
||||||
SMTPHost *string `json:"SMTP_HOST"`
|
SMTPHost *string `json:"SMTP_HOST,omitempty"`
|
||||||
SMTPPort *string `json:"SMTP_PORT"`
|
SMTPPort *string `json:"SMTP_PORT,omitempty"`
|
||||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
SMTPUsername *string `json:"SMTP_USERNAME,omitempty"`
|
||||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
SMTPPassword *string `json:"SMTP_PASSWORD,omitempty"`
|
||||||
SMTPLocalName *string `json:"SMTP_LOCAL_NAME"`
|
SMTPLocalName *string `json:"SMTP_LOCAL_NAME,omitempty"`
|
||||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
SenderEmail *string `json:"SENDER_EMAIL,omitempty"`
|
||||||
SenderName *string `json:"SENDER_NAME"`
|
SenderName *string `json:"SENDER_NAME,omitempty"`
|
||||||
JwtType *string `json:"JWT_TYPE"`
|
JwtType *string `json:"JWT_TYPE,omitempty"`
|
||||||
JwtSecret *string `json:"JWT_SECRET"`
|
JwtSecret *string `json:"JWT_SECRET,omitempty"`
|
||||||
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY"`
|
JwtPrivateKey *string `json:"JWT_PRIVATE_KEY,omitempty"`
|
||||||
JwtPublicKey *string `json:"JWT_PUBLIC_KEY"`
|
JwtPublicKey *string `json:"JWT_PUBLIC_KEY,omitempty"`
|
||||||
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
|
AllowedOrigins []string `json:"ALLOWED_ORIGINS,omitempty"`
|
||||||
AppURL *string `json:"APP_URL"`
|
AppURL *string `json:"APP_URL,omitempty"`
|
||||||
ResetPasswordURL *string `json:"RESET_PASSWORD_URL"`
|
ResetPasswordURL *string `json:"RESET_PASSWORD_URL,omitempty"`
|
||||||
AppCookieSecure *bool `json:"APP_COOKIE_SECURE"`
|
AppCookieSecure *bool `json:"APP_COOKIE_SECURE,omitempty"`
|
||||||
AdminCookieSecure *bool `json:"ADMIN_COOKIE_SECURE"`
|
AdminCookieSecure *bool `json:"ADMIN_COOKIE_SECURE,omitempty"`
|
||||||
DisableEmailVerification *bool `json:"DISABLE_EMAIL_VERIFICATION"`
|
DisableEmailVerification *bool `json:"DISABLE_EMAIL_VERIFICATION,omitempty"`
|
||||||
DisableBasicAuthentication *bool `json:"DISABLE_BASIC_AUTHENTICATION"`
|
DisableBasicAuthentication *bool `json:"DISABLE_BASIC_AUTHENTICATION,omitempty"`
|
||||||
DisableMagicLinkLogin *bool `json:"DISABLE_MAGIC_LINK_LOGIN"`
|
DisableMagicLinkLogin *bool `json:"DISABLE_MAGIC_LINK_LOGIN,omitempty"`
|
||||||
DisableLoginPage *bool `json:"DISABLE_LOGIN_PAGE"`
|
DisableLoginPage *bool `json:"DISABLE_LOGIN_PAGE,omitempty"`
|
||||||
DisableSignUp *bool `json:"DISABLE_SIGN_UP"`
|
DisableSignUp *bool `json:"DISABLE_SIGN_UP,omitempty"`
|
||||||
DisableRedisForEnv *bool `json:"DISABLE_REDIS_FOR_ENV"`
|
DisableRedisForEnv *bool `json:"DISABLE_REDIS_FOR_ENV,omitempty"`
|
||||||
DisableStrongPassword *bool `json:"DISABLE_STRONG_PASSWORD"`
|
DisableStrongPassword *bool `json:"DISABLE_STRONG_PASSWORD,omitempty"`
|
||||||
DisableMultiFactorAuthentication *bool `json:"DISABLE_MULTI_FACTOR_AUTHENTICATION"`
|
DisableMultiFactorAuthentication *bool `json:"DISABLE_MULTI_FACTOR_AUTHENTICATION,omitempty"`
|
||||||
EnforceMultiFactorAuthentication *bool `json:"ENFORCE_MULTI_FACTOR_AUTHENTICATION"`
|
EnforceMultiFactorAuthentication *bool `json:"ENFORCE_MULTI_FACTOR_AUTHENTICATION,omitempty"`
|
||||||
Roles []string `json:"ROLES"`
|
Roles []string `json:"ROLES,omitempty"`
|
||||||
ProtectedRoles []string `json:"PROTECTED_ROLES"`
|
ProtectedRoles []string `json:"PROTECTED_ROLES,omitempty"`
|
||||||
DefaultRoles []string `json:"DEFAULT_ROLES"`
|
DefaultRoles []string `json:"DEFAULT_ROLES,omitempty"`
|
||||||
JwtRoleClaim *string `json:"JWT_ROLE_CLAIM"`
|
JwtRoleClaim *string `json:"JWT_ROLE_CLAIM,omitempty"`
|
||||||
GoogleClientID *string `json:"GOOGLE_CLIENT_ID"`
|
GoogleClientID *string `json:"GOOGLE_CLIENT_ID,omitempty"`
|
||||||
GoogleClientSecret *string `json:"GOOGLE_CLIENT_SECRET"`
|
GoogleClientSecret *string `json:"GOOGLE_CLIENT_SECRET,omitempty"`
|
||||||
GithubClientID *string `json:"GITHUB_CLIENT_ID"`
|
GithubClientID *string `json:"GITHUB_CLIENT_ID,omitempty"`
|
||||||
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET"`
|
GithubClientSecret *string `json:"GITHUB_CLIENT_SECRET,omitempty"`
|
||||||
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID"`
|
FacebookClientID *string `json:"FACEBOOK_CLIENT_ID,omitempty"`
|
||||||
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET"`
|
FacebookClientSecret *string `json:"FACEBOOK_CLIENT_SECRET,omitempty"`
|
||||||
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID"`
|
LinkedinClientID *string `json:"LINKEDIN_CLIENT_ID,omitempty"`
|
||||||
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET"`
|
LinkedinClientSecret *string `json:"LINKEDIN_CLIENT_SECRET,omitempty"`
|
||||||
AppleClientID *string `json:"APPLE_CLIENT_ID"`
|
AppleClientID *string `json:"APPLE_CLIENT_ID,omitempty"`
|
||||||
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET"`
|
AppleClientSecret *string `json:"APPLE_CLIENT_SECRET,omitempty"`
|
||||||
TwitterClientID *string `json:"TWITTER_CLIENT_ID"`
|
DiscordClientID *string `json:"DISCORD_CLIENT_ID,omitempty"`
|
||||||
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET"`
|
DiscordClientSecret *string `json:"DISCORD_CLIENT_SECRET,omitempty"`
|
||||||
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID"`
|
TwitterClientID *string `json:"TWITTER_CLIENT_ID,omitempty"`
|
||||||
MicrosoftClientSecret *string `json:"MICROSOFT_CLIENT_SECRET"`
|
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET,omitempty"`
|
||||||
MicrosoftActiveDirectoryTenantID *string `json:"MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID"`
|
MicrosoftClientID *string `json:"MICROSOFT_CLIENT_ID,omitempty"`
|
||||||
OrganizationName *string `json:"ORGANIZATION_NAME"`
|
MicrosoftClientSecret *string `json:"MICROSOFT_CLIENT_SECRET,omitempty"`
|
||||||
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
|
MicrosoftActiveDirectoryTenantID *string `json:"MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID,omitempty"`
|
||||||
DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE"`
|
TwitchClientID *string `json:"TWITCH_CLIENT_ID,omitempty"`
|
||||||
DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE"`
|
TwitchClientSecret *string `json:"TWITCH_CLIENT_SECRET,omitempty"`
|
||||||
|
OrganizationName *string `json:"ORGANIZATION_NAME,omitempty"`
|
||||||
|
OrganizationLogo *string `json:"ORGANIZATION_LOGO,omitempty"`
|
||||||
|
DefaultAuthorizeResponseType *string `json:"DEFAULT_AUTHORIZE_RESPONSE_TYPE,omitempty"`
|
||||||
|
DefaultAuthorizeResponseMode *string `json:"DEFAULT_AUTHORIZE_RESPONSE_MODE,omitempty"`
|
||||||
|
DisablePlayground *bool `json:"DISABLE_PLAYGROUND,omitempty"`
|
||||||
|
DisableMailOtpLogin *bool `json:"DISABLE_MAIL_OTP_LOGIN,omitempty"`
|
||||||
|
DisableTotpLogin *bool `json:"DISABLE_TOTP_LOGIN,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateProfileInput struct {
|
type UpdateProfileInput struct {
|
||||||
OldPassword *string `json:"old_password"`
|
OldPassword *string `json:"old_password,omitempty"`
|
||||||
NewPassword *string `json:"new_password"`
|
NewPassword *string `json:"new_password,omitempty"`
|
||||||
ConfirmNewPassword *string `json:"confirm_new_password"`
|
ConfirmNewPassword *string `json:"confirm_new_password,omitempty"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
GivenName *string `json:"given_name"`
|
GivenName *string `json:"given_name,omitempty"`
|
||||||
FamilyName *string `json:"family_name"`
|
FamilyName *string `json:"family_name,omitempty"`
|
||||||
MiddleName *string `json:"middle_name"`
|
MiddleName *string `json:"middle_name,omitempty"`
|
||||||
Nickname *string `json:"nickname"`
|
Nickname *string `json:"nickname,omitempty"`
|
||||||
Gender *string `json:"gender"`
|
Gender *string `json:"gender,omitempty"`
|
||||||
Birthdate *string `json:"birthdate"`
|
Birthdate *string `json:"birthdate,omitempty"`
|
||||||
PhoneNumber *string `json:"phone_number"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
Picture *string `json:"picture"`
|
Picture *string `json:"picture,omitempty"`
|
||||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled,omitempty"`
|
||||||
|
AppData map[string]interface{} `json:"app_data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateUserInput struct {
|
type UpdateUserInput struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
EmailVerified *bool `json:"email_verified"`
|
EmailVerified *bool `json:"email_verified,omitempty"`
|
||||||
GivenName *string `json:"given_name"`
|
GivenName *string `json:"given_name,omitempty"`
|
||||||
FamilyName *string `json:"family_name"`
|
FamilyName *string `json:"family_name,omitempty"`
|
||||||
MiddleName *string `json:"middle_name"`
|
MiddleName *string `json:"middle_name,omitempty"`
|
||||||
Nickname *string `json:"nickname"`
|
Nickname *string `json:"nickname,omitempty"`
|
||||||
Gender *string `json:"gender"`
|
Gender *string `json:"gender,omitempty"`
|
||||||
Birthdate *string `json:"birthdate"`
|
Birthdate *string `json:"birthdate,omitempty"`
|
||||||
PhoneNumber *string `json:"phone_number"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
Picture *string `json:"picture"`
|
Picture *string `json:"picture,omitempty"`
|
||||||
Roles []*string `json:"roles"`
|
Roles []*string `json:"roles,omitempty"`
|
||||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled,omitempty"`
|
||||||
|
AppData map[string]interface{} `json:"app_data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateWebhookRequest struct {
|
type UpdateWebhookRequest struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
EventName *string `json:"event_name"`
|
EventName *string `json:"event_name,omitempty"`
|
||||||
EventDescription *string `json:"event_description"`
|
EventDescription *string `json:"event_description,omitempty"`
|
||||||
Endpoint *string `json:"endpoint"`
|
Endpoint *string `json:"endpoint,omitempty"`
|
||||||
Enabled *bool `json:"enabled"`
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
Headers map[string]interface{} `json:"headers"`
|
Headers map[string]interface{} `json:"headers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Email string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
EmailVerified bool `json:"email_verified"`
|
||||||
SignupMethods string `json:"signup_methods"`
|
SignupMethods string `json:"signup_methods"`
|
||||||
GivenName *string `json:"given_name"`
|
GivenName *string `json:"given_name,omitempty"`
|
||||||
FamilyName *string `json:"family_name"`
|
FamilyName *string `json:"family_name,omitempty"`
|
||||||
MiddleName *string `json:"middle_name"`
|
MiddleName *string `json:"middle_name,omitempty"`
|
||||||
Nickname *string `json:"nickname"`
|
Nickname *string `json:"nickname,omitempty"`
|
||||||
PreferredUsername *string `json:"preferred_username"`
|
PreferredUsername *string `json:"preferred_username,omitempty"`
|
||||||
Gender *string `json:"gender"`
|
Gender *string `json:"gender,omitempty"`
|
||||||
Birthdate *string `json:"birthdate"`
|
Birthdate *string `json:"birthdate,omitempty"`
|
||||||
PhoneNumber *string `json:"phone_number"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
PhoneNumberVerified *bool `json:"phone_number_verified"`
|
PhoneNumberVerified *bool `json:"phone_number_verified,omitempty"`
|
||||||
Picture *string `json:"picture"`
|
Picture *string `json:"picture,omitempty"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at,omitempty"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at,omitempty"`
|
||||||
RevokedTimestamp *int64 `json:"revoked_timestamp"`
|
RevokedTimestamp *int64 `json:"revoked_timestamp,omitempty"`
|
||||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled,omitempty"`
|
||||||
|
AppData map[string]interface{} `json:"app_data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Users struct {
|
type Users struct {
|
||||||
@@ -449,33 +486,34 @@ type Users struct {
|
|||||||
type ValidateJWTTokenInput struct {
|
type ValidateJWTTokenInput struct {
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidateJWTTokenResponse struct {
|
type ValidateJWTTokenResponse struct {
|
||||||
IsValid bool `json:"is_valid"`
|
IsValid bool `json:"is_valid"`
|
||||||
Claims map[string]interface{} `json:"claims"`
|
Claims map[string]interface{} `json:"claims,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidateSessionInput struct {
|
type ValidateSessionInput struct {
|
||||||
Cookie string `json:"cookie"`
|
Cookie string `json:"cookie"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidateSessionResponse struct {
|
type ValidateSessionResponse struct {
|
||||||
IsValid bool `json:"is_valid"`
|
IsValid bool `json:"is_valid"`
|
||||||
|
User *User `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Identifier *string `json:"identifier"`
|
Identifier *string `json:"identifier,omitempty"`
|
||||||
Token *string `json:"token"`
|
Token *string `json:"token,omitempty"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
Expires *int64 `json:"expires"`
|
Expires *int64 `json:"expires,omitempty"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at,omitempty"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at,omitempty"`
|
||||||
Nonce *string `json:"nonce"`
|
Nonce *string `json:"nonce,omitempty"`
|
||||||
RedirectURI *string `json:"redirect_uri"`
|
RedirectURI *string `json:"redirect_uri,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequests struct {
|
type VerificationRequests struct {
|
||||||
@@ -485,35 +523,36 @@ type VerificationRequests struct {
|
|||||||
|
|
||||||
type VerifyEmailInput struct {
|
type VerifyEmailInput struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
State *string `json:"state"`
|
State *string `json:"state,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerifyOTPRequest struct {
|
type VerifyOTPRequest struct {
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email,omitempty"`
|
||||||
PhoneNumber *string `json:"phone_number"`
|
PhoneNumber *string `json:"phone_number,omitempty"`
|
||||||
Otp string `json:"otp"`
|
Otp string `json:"otp"`
|
||||||
State *string `json:"state"`
|
IsTotp *bool `json:"is_totp,omitempty"`
|
||||||
|
State *string `json:"state,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Webhook struct {
|
type Webhook struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
EventName *string `json:"event_name"`
|
EventName *string `json:"event_name,omitempty"`
|
||||||
EventDescription *string `json:"event_description"`
|
EventDescription *string `json:"event_description,omitempty"`
|
||||||
Endpoint *string `json:"endpoint"`
|
Endpoint *string `json:"endpoint,omitempty"`
|
||||||
Enabled *bool `json:"enabled"`
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
Headers map[string]interface{} `json:"headers"`
|
Headers map[string]interface{} `json:"headers,omitempty"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at,omitempty"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebhookLog struct {
|
type WebhookLog struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
HTTPStatus *int64 `json:"http_status"`
|
HTTPStatus *int64 `json:"http_status,omitempty"`
|
||||||
Response *string `json:"response"`
|
Response *string `json:"response,omitempty"`
|
||||||
Request *string `json:"request"`
|
Request *string `json:"request,omitempty"`
|
||||||
WebhookID *string `json:"webhook_id"`
|
WebhookID *string `json:"webhook_id,omitempty"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at,omitempty"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebhookLogs struct {
|
type WebhookLogs struct {
|
||||||
|
@@ -20,19 +20,24 @@ type Meta {
|
|||||||
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_discord_login_enabled: Boolean!
|
||||||
is_twitter_login_enabled: Boolean!
|
is_twitter_login_enabled: Boolean!
|
||||||
is_microsoft_login_enabled: Boolean!
|
is_microsoft_login_enabled: Boolean!
|
||||||
|
is_twitch_login_enabled: Boolean!
|
||||||
is_email_verification_enabled: Boolean!
|
is_email_verification_enabled: Boolean!
|
||||||
is_basic_authentication_enabled: Boolean!
|
is_basic_authentication_enabled: Boolean!
|
||||||
is_magic_link_login_enabled: Boolean!
|
is_magic_link_login_enabled: Boolean!
|
||||||
is_sign_up_enabled: Boolean!
|
is_sign_up_enabled: Boolean!
|
||||||
is_strong_password_enabled: Boolean!
|
is_strong_password_enabled: Boolean!
|
||||||
is_multi_factor_auth_enabled: Boolean!
|
is_multi_factor_auth_enabled: Boolean!
|
||||||
|
is_mobile_basic_authentication_enabled: Boolean!
|
||||||
|
is_phone_verification_enabled: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
id: ID!
|
id: ID!
|
||||||
email: String!
|
# email or phone_number is always present
|
||||||
|
email: String
|
||||||
email_verified: Boolean!
|
email_verified: Boolean!
|
||||||
signup_methods: String!
|
signup_methods: String!
|
||||||
given_name: String
|
given_name: String
|
||||||
@@ -51,6 +56,7 @@ type User {
|
|||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
revoked_timestamp: Int64
|
revoked_timestamp: Int64
|
||||||
is_multi_factor_auth_enabled: Boolean
|
is_multi_factor_auth_enabled: Boolean
|
||||||
|
app_data: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
type Users {
|
type Users {
|
||||||
@@ -93,17 +99,30 @@ type AuthResponse {
|
|||||||
message: String!
|
message: String!
|
||||||
should_show_email_otp_screen: Boolean
|
should_show_email_otp_screen: Boolean
|
||||||
should_show_mobile_otp_screen: Boolean
|
should_show_mobile_otp_screen: Boolean
|
||||||
|
should_show_totp_screen: Boolean
|
||||||
access_token: String
|
access_token: String
|
||||||
id_token: String
|
id_token: String
|
||||||
refresh_token: String
|
refresh_token: String
|
||||||
expires_in: Int64
|
expires_in: Int64
|
||||||
user: User
|
user: User
|
||||||
|
# key for totp login
|
||||||
|
# it is a base64 image url
|
||||||
|
authenticator_scanner_image: String
|
||||||
|
# string which can be used instead of scanner image
|
||||||
|
authenticator_secret: String
|
||||||
|
# recovery codes for totp login shared with user only once
|
||||||
|
authenticator_recovery_codes: [String]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response {
|
type Response {
|
||||||
message: String!
|
message: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ForgotPasswordResponse {
|
||||||
|
message: String!
|
||||||
|
should_show_mobile_otp_screen: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
type InviteMembersResponse {
|
type InviteMembersResponse {
|
||||||
message: String!
|
message: String!
|
||||||
Users: [User!]!
|
Users: [User!]!
|
||||||
@@ -160,17 +179,24 @@ type Env {
|
|||||||
LINKEDIN_CLIENT_SECRET: String
|
LINKEDIN_CLIENT_SECRET: String
|
||||||
APPLE_CLIENT_ID: String
|
APPLE_CLIENT_ID: String
|
||||||
APPLE_CLIENT_SECRET: String
|
APPLE_CLIENT_SECRET: String
|
||||||
|
DISCORD_CLIENT_ID: String
|
||||||
|
DISCORD_CLIENT_SECRET: String
|
||||||
TWITTER_CLIENT_ID: String
|
TWITTER_CLIENT_ID: String
|
||||||
TWITTER_CLIENT_SECRET: String
|
TWITTER_CLIENT_SECRET: String
|
||||||
MICROSOFT_CLIENT_ID: String
|
MICROSOFT_CLIENT_ID: String
|
||||||
MICROSOFT_CLIENT_SECRET: String
|
MICROSOFT_CLIENT_SECRET: String
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: String
|
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: String
|
||||||
|
TWITCH_CLIENT_ID: String
|
||||||
|
TWITCH_CLIENT_SECRET: String
|
||||||
ORGANIZATION_NAME: String
|
ORGANIZATION_NAME: String
|
||||||
ORGANIZATION_LOGO: String
|
ORGANIZATION_LOGO: String
|
||||||
APP_COOKIE_SECURE: Boolean!
|
APP_COOKIE_SECURE: Boolean!
|
||||||
ADMIN_COOKIE_SECURE: Boolean!
|
ADMIN_COOKIE_SECURE: Boolean!
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
|
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE: String
|
DEFAULT_AUTHORIZE_RESPONSE_MODE: String
|
||||||
|
DISABLE_PLAYGROUND: Boolean!
|
||||||
|
DISABLE_MAIL_OTP_LOGIN: Boolean!
|
||||||
|
DISABLE_TOTP_LOGIN: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidateJWTTokenResponse {
|
type ValidateJWTTokenResponse {
|
||||||
@@ -180,6 +206,7 @@ type ValidateJWTTokenResponse {
|
|||||||
|
|
||||||
type ValidateSessionResponse {
|
type ValidateSessionResponse {
|
||||||
is_valid: Boolean!
|
is_valid: Boolean!
|
||||||
|
user: User!
|
||||||
}
|
}
|
||||||
|
|
||||||
type GenerateJWTKeysResponse {
|
type GenerateJWTKeysResponse {
|
||||||
@@ -283,15 +310,22 @@ input UpdateEnvInput {
|
|||||||
LINKEDIN_CLIENT_SECRET: String
|
LINKEDIN_CLIENT_SECRET: String
|
||||||
APPLE_CLIENT_ID: String
|
APPLE_CLIENT_ID: String
|
||||||
APPLE_CLIENT_SECRET: String
|
APPLE_CLIENT_SECRET: String
|
||||||
|
DISCORD_CLIENT_ID: String
|
||||||
|
DISCORD_CLIENT_SECRET: String
|
||||||
TWITTER_CLIENT_ID: String
|
TWITTER_CLIENT_ID: String
|
||||||
TWITTER_CLIENT_SECRET: String
|
TWITTER_CLIENT_SECRET: String
|
||||||
MICROSOFT_CLIENT_ID: String
|
MICROSOFT_CLIENT_ID: String
|
||||||
MICROSOFT_CLIENT_SECRET: String
|
MICROSOFT_CLIENT_SECRET: String
|
||||||
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: String
|
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: String
|
||||||
|
TWITCH_CLIENT_ID: String
|
||||||
|
TWITCH_CLIENT_SECRET: String
|
||||||
ORGANIZATION_NAME: String
|
ORGANIZATION_NAME: String
|
||||||
ORGANIZATION_LOGO: String
|
ORGANIZATION_LOGO: String
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
|
DEFAULT_AUTHORIZE_RESPONSE_TYPE: String
|
||||||
DEFAULT_AUTHORIZE_RESPONSE_MODE: String
|
DEFAULT_AUTHORIZE_RESPONSE_MODE: String
|
||||||
|
DISABLE_PLAYGROUND: Boolean
|
||||||
|
DISABLE_MAIL_OTP_LOGIN: Boolean
|
||||||
|
DISABLE_TOTP_LOGIN: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
input AdminLoginInput {
|
input AdminLoginInput {
|
||||||
@@ -302,6 +336,7 @@ input AdminSignupInput {
|
|||||||
admin_secret: String!
|
admin_secret: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Deprecated from v1.2.0
|
||||||
input MobileSignUpInput {
|
input MobileSignUpInput {
|
||||||
email: String
|
email: String
|
||||||
given_name: String
|
given_name: String
|
||||||
@@ -322,10 +357,11 @@ input MobileSignUpInput {
|
|||||||
# it is used to get code for an on-going auth process during login
|
# it is used to get code for an on-going auth process during login
|
||||||
# and use that code for setting `c_hash` in id_token
|
# and use that code for setting `c_hash` in id_token
|
||||||
state: String
|
state: String
|
||||||
|
app_data: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
input SignUpInput {
|
input SignUpInput {
|
||||||
email: String!
|
email: String
|
||||||
given_name: String
|
given_name: String
|
||||||
family_name: String
|
family_name: String
|
||||||
middle_name: String
|
middle_name: String
|
||||||
@@ -344,10 +380,12 @@ input SignUpInput {
|
|||||||
# it is used to get code for an on-going auth process during login
|
# it is used to get code for an on-going auth process during login
|
||||||
# and use that code for setting `c_hash` in id_token
|
# and use that code for setting `c_hash` in id_token
|
||||||
state: String
|
state: String
|
||||||
|
app_data: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
input LoginInput {
|
input LoginInput {
|
||||||
email: String!
|
email: String
|
||||||
|
phone_number: String
|
||||||
password: String!
|
password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
@@ -357,6 +395,7 @@ input LoginInput {
|
|||||||
state: String
|
state: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Deprecated from v1.2.0
|
||||||
input MobileLoginInput {
|
input MobileLoginInput {
|
||||||
phone_number: String!
|
phone_number: String!
|
||||||
password: String!
|
password: String!
|
||||||
@@ -399,6 +438,7 @@ input UpdateProfileInput {
|
|||||||
phone_number: String
|
phone_number: String
|
||||||
picture: String
|
picture: String
|
||||||
is_multi_factor_auth_enabled: Boolean
|
is_multi_factor_auth_enabled: Boolean
|
||||||
|
app_data: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateUserInput {
|
input UpdateUserInput {
|
||||||
@@ -415,16 +455,20 @@ input UpdateUserInput {
|
|||||||
picture: String
|
picture: String
|
||||||
roles: [String]
|
roles: [String]
|
||||||
is_multi_factor_auth_enabled: Boolean
|
is_multi_factor_auth_enabled: Boolean
|
||||||
|
app_data: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
input ForgotPasswordInput {
|
input ForgotPasswordInput {
|
||||||
email: String!
|
email: String
|
||||||
|
phone_number: String
|
||||||
state: String
|
state: String
|
||||||
redirect_uri: String
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResetPasswordInput {
|
input ResetPasswordInput {
|
||||||
token: String!
|
token: String
|
||||||
|
otp: String
|
||||||
|
phone_number: String
|
||||||
password: String!
|
password: String!
|
||||||
confirm_password: String!
|
confirm_password: String!
|
||||||
}
|
}
|
||||||
@@ -512,6 +556,7 @@ input WebhookRequest {
|
|||||||
input TestEndpointRequest {
|
input TestEndpointRequest {
|
||||||
endpoint: String!
|
endpoint: String!
|
||||||
event_name: String!
|
event_name: String!
|
||||||
|
event_description: String
|
||||||
headers: Map
|
headers: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,10 +584,11 @@ input DeleteEmailTemplateRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input VerifyOTPRequest {
|
input VerifyOTPRequest {
|
||||||
# either email or phone_number is required
|
# either email, phone_number or totp_token is required
|
||||||
email: String
|
email: String
|
||||||
phone_number: String
|
phone_number: String
|
||||||
otp: String!
|
otp: String!
|
||||||
|
is_totp: Boolean
|
||||||
# state is used for authorization code grant flow
|
# state is used for authorization code grant flow
|
||||||
# it is used to get code for an on-going auth process during login
|
# it is used to get code for an on-going auth process during login
|
||||||
# and use that code for setting `c_hash` in id_token
|
# and use that code for setting `c_hash` in id_token
|
||||||
@@ -564,20 +610,24 @@ input GetUserRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
is_registered(email: String): AuthResponse! # custom api
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
|
# Deprecated from v1.2.0
|
||||||
mobile_signup(params: MobileSignUpInput): AuthResponse!
|
mobile_signup(params: MobileSignUpInput): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
|
# Deprecated from v1.2.0
|
||||||
mobile_login(params: MobileLoginInput!): AuthResponse!
|
mobile_login(params: MobileLoginInput!): 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!): ForgotPasswordResponse!
|
||||||
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!
|
||||||
|
deactivate_account: 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!
|
||||||
|
@@ -2,6 +2,7 @@ package graph
|
|||||||
|
|
||||||
// This file will be automatically regenerated based on the schema, any resolver implementations
|
// This file will be automatically regenerated based on the schema, any resolver implementations
|
||||||
// will be copied through when generating and any unknown code will be moved to the end.
|
// will be copied through when generating and any unknown code will be moved to the end.
|
||||||
|
// Code generated by github.com/99designs/gqlgen version v0.17.39
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -11,6 +12,11 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Signup is the resolver for the signup field.
|
||||||
|
func (r *queryResolver) IsRegistered(ctx context.Context, email string) (*model.AuthResponse, error) {
|
||||||
|
return resolvers.IsRegisteredResolver(ctx, email)
|
||||||
|
}
|
||||||
|
|
||||||
// Signup is the resolver for the signup field.
|
// Signup is the resolver for the signup field.
|
||||||
func (r *mutationResolver) Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error) {
|
func (r *mutationResolver) Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error) {
|
||||||
return resolvers.SignupResolver(ctx, params)
|
return resolvers.SignupResolver(ctx, params)
|
||||||
@@ -57,7 +63,7 @@ func (r *mutationResolver) ResendVerifyEmail(ctx context.Context, params model.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ForgotPassword is the resolver for the forgot_password field.
|
// ForgotPassword is the resolver for the forgot_password field.
|
||||||
func (r *mutationResolver) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
|
func (r *mutationResolver) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error) {
|
||||||
return resolvers.ForgotPasswordResolver(ctx, params)
|
return resolvers.ForgotPasswordResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +87,11 @@ func (r *mutationResolver) ResendOtp(ctx context.Context, params model.ResendOTP
|
|||||||
return resolvers.ResendOTPResolver(ctx, params)
|
return resolvers.ResendOTPResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeactivateAccount is the resolver for the deactivate_account field.
|
||||||
|
func (r *mutationResolver) DeactivateAccount(ctx context.Context) (*model.Response, error) {
|
||||||
|
return resolvers.DeactivateAccountResolver(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteUser is the resolver for the _delete_user field.
|
// DeleteUser is the resolver for the _delete_user field.
|
||||||
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
||||||
return resolvers.DeleteUserResolver(ctx, params)
|
return resolvers.DeleteUserResolver(ctx, params)
|
||||||
|
@@ -55,6 +55,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
authorizeWebMessageTemplate = "authorize_web_message.tmpl"
|
authorizeWebMessageTemplate = "authorize_web_message.tmpl"
|
||||||
authorizeFormPostTemplate = "authorize_form_post.tmpl"
|
authorizeFormPostTemplate = "authorize_form_post.tmpl"
|
||||||
|
baseAppPath = "/app"
|
||||||
|
signupPath = "/app/signup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthorizeHandler is the handler for the /authorize route
|
// AuthorizeHandler is the handler for the /authorize route
|
||||||
@@ -74,6 +76,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
clientID := strings.TrimSpace(gc.Query("client_id"))
|
clientID := strings.TrimSpace(gc.Query("client_id"))
|
||||||
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
||||||
nonce := strings.TrimSpace(gc.Query("nonce"))
|
nonce := strings.TrimSpace(gc.Query("nonce"))
|
||||||
|
screenHint := strings.TrimSpace(gc.Query("screen_hint"))
|
||||||
|
|
||||||
var scope []string
|
var scope []string
|
||||||
if scopeString == "" {
|
if scopeString == "" {
|
||||||
@@ -120,27 +123,33 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
// TODO add state with timeout
|
// TODO add state with timeout
|
||||||
// used for response mode query or fragment
|
// used for response mode query or fragment
|
||||||
loginState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
authState := "state=" + state + "&scope=" + scopeString + "&redirect_uri=" + redirectURI
|
||||||
if responseType == constants.ResponseTypeCode {
|
if responseType == constants.ResponseTypeCode {
|
||||||
loginState += "&code=" + code
|
authState += "&code=" + code
|
||||||
if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil {
|
if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil {
|
||||||
log.Debug("Error setting temp code", err)
|
log.Debug("Error setting temp code", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loginState += "&nonce=" + nonce
|
authState += "&nonce=" + nonce
|
||||||
if err := memorystore.Provider.SetState(state, nonce); err != nil {
|
if err := memorystore.Provider.SetState(state, nonce); err != nil {
|
||||||
log.Debug("Error setting temp code", err)
|
log.Debug("Error setting temp code", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loginURL := "/app?" + loginState
|
authURL := baseAppPath + "?" + authState
|
||||||
|
|
||||||
if responseMode == constants.ResponseModeFragment {
|
if screenHint == constants.ScreenHintSignUp {
|
||||||
loginURL = "/app#" + loginState
|
authURL = signupPath + "?" + authState
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseMode == constants.ResponseModeFragment && screenHint == constants.ScreenHintSignUp {
|
||||||
|
authURL = signupPath + "#" + authState
|
||||||
|
} else if responseMode == constants.ResponseModeFragment {
|
||||||
|
authURL = baseAppPath + "#" + authState
|
||||||
}
|
}
|
||||||
|
|
||||||
if responseType == constants.ResponseTypeCode && codeChallenge == "" {
|
if responseType == constants.ResponseTypeCode && codeChallenge == "" {
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||||
"type": "authorization_response",
|
"type": "authorization_response",
|
||||||
"response": map[string]interface{}{
|
"response": map[string]interface{}{
|
||||||
"error": "code_challenge_required",
|
"error": "code_challenge_required",
|
||||||
@@ -160,7 +169,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
sessionToken, err := cookie.GetSession(gc)
|
sessionToken, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("GetSession failed: ", err)
|
log.Debug("GetSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +177,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("ValidateBrowserSession failed: ", err)
|
log.Debug("ValidateBrowserSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +185,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
user, err := db.Provider.GetUserByID(gc, userID)
|
user, err := db.Provider.GetUserByID(gc, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("GetUserByID failed: ", err)
|
log.Debug("GetUserByID failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||||
"type": "authorization_response",
|
"type": "authorization_response",
|
||||||
"response": map[string]interface{}{
|
"response": map[string]interface{}{
|
||||||
"error": "signup_required",
|
"error": "signup_required",
|
||||||
@@ -197,27 +206,27 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
newSessionTokenData, newSessionToken, newSessionExpiresAt, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
|
newSessionTokenData, newSessionToken, newSessionExpiresAt, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("CreateSessionToken failed: ", err)
|
log.Debug("CreateSessionToken failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add state with timeout
|
// TODO: add state with timeout
|
||||||
// if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
|
// if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
|
||||||
// log.Debug("SetState failed: ", err)
|
// log.Debug("SetState failed: ", err)
|
||||||
// handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
// handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// TODO: add state with timeout
|
// TODO: add state with timeout
|
||||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+newSessionToken); err != nil {
|
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+newSessionToken); err != nil {
|
||||||
log.Debug("SetState failed: ", err)
|
log.Debug("SetState failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken, newSessionExpiresAt); err != nil {
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken, newSessionExpiresAt); err != nil {
|
||||||
log.Debug("SetUserSession failed: ", err)
|
log.Debug("SetUserSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +260,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||||
"type": "authorization_response",
|
"type": "authorization_response",
|
||||||
"response": map[string]interface{}{
|
"response": map[string]interface{}{
|
||||||
"code": code,
|
"code": code,
|
||||||
@@ -267,19 +276,19 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod, nonce, "")
|
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod, nonce, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("CreateAuthToken failed: ", err)
|
log.Debug("CreateAuthToken failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt); err != nil {
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt); err != nil {
|
||||||
log.Debug("SetUserSession failed: ", err)
|
log.Debug("SetUserSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt); err != nil {
|
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt); err != nil {
|
||||||
log.Debug("SetUserSession failed: ", err)
|
log.Debug("SetUserSession failed: ", err)
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,14 +331,14 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
handleResponse(gc, responseMode, authURL, redirectURI, map[string]interface{}{
|
||||||
"type": "authorization_response",
|
"type": "authorization_response",
|
||||||
"response": res,
|
"response": res,
|
||||||
}, http.StatusOK)
|
}, http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
handleResponse(gc, responseMode, authURL, redirectURI, loginError, http.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,14 +361,14 @@ func validateAuthorizeRequest(responseType, responseMode, clientID, state, codeC
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleResponse(gc *gin.Context, responseMode, loginURI, redirectURI string, data map[string]interface{}, httpStatusCode int) {
|
func handleResponse(gc *gin.Context, responseMode, authURI, redirectURI string, data map[string]interface{}, httpStatusCode int) {
|
||||||
isAuthenticationRequired := false
|
isAuthenticationRequired := false
|
||||||
if _, ok := data["response"].(map[string]interface{})["error"]; ok {
|
if _, ok := data["response"].(map[string]interface{})["error"]; ok {
|
||||||
isAuthenticationRequired = true
|
isAuthenticationRequired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if isAuthenticationRequired && responseMode != constants.ResponseModeWebMessage {
|
if isAuthenticationRequired && responseMode != constants.ResponseModeWebMessage {
|
||||||
gc.Redirect(http.StatusFound, loginURI)
|
gc.Redirect(http.StatusFound, authURI)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,15 +7,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/oauth2"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
@@ -23,6 +24,7 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"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/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"
|
||||||
)
|
)
|
||||||
@@ -32,11 +34,11 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
return func(ctx *gin.Context) {
|
return func(ctx *gin.Context) {
|
||||||
provider := ctx.Param("oauth_provider")
|
provider := ctx.Param("oauth_provider")
|
||||||
state := ctx.Request.FormValue("state")
|
state := ctx.Request.FormValue("state")
|
||||||
|
|
||||||
sessionState, err := memorystore.Provider.GetState(state)
|
sessionState, err := memorystore.Provider.GetState(state)
|
||||||
if sessionState == "" || err != nil {
|
if sessionState == "" || err != nil {
|
||||||
log.Debug("Invalid oauth state: ", state)
|
log.Debug("Invalid oauth state: ", state)
|
||||||
ctx.JSON(400, gin.H{"error": "invalid oauth state"})
|
ctx.JSON(400, gin.H{"error": "invalid oauth state"})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// contains random token, redirect url, role
|
// contains random token, redirect url, role
|
||||||
sessionSplit := strings.Split(state, "___")
|
sessionSplit := strings.Split(state, "___")
|
||||||
@@ -46,32 +48,47 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
ctx.JSON(400, gin.H{"error": "invalid redirect url"})
|
ctx.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove state from store
|
// remove state from store
|
||||||
go memorystore.Provider.RemoveState(state)
|
go memorystore.Provider.RemoveState(state)
|
||||||
|
|
||||||
stateValue := sessionSplit[0]
|
stateValue := sessionSplit[0]
|
||||||
redirectURL := sessionSplit[1]
|
redirectURL := sessionSplit[1]
|
||||||
inputRoles := strings.Split(sessionSplit[2], ",")
|
inputRoles := strings.Split(sessionSplit[2], ",")
|
||||||
scopes := strings.Split(sessionSplit[3], ",")
|
scopeString := sessionSplit[3]
|
||||||
|
scopes := []string{}
|
||||||
|
if scopeString != "" {
|
||||||
|
if strings.Contains(scopeString, ",") {
|
||||||
|
scopes = strings.Split(scopeString, ",")
|
||||||
|
}
|
||||||
|
if strings.Contains(scopeString, " ") {
|
||||||
|
scopes = strings.Split(scopeString, " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
var user *models.User
|
var user *models.User
|
||||||
oauthCode := ctx.Request.FormValue("code")
|
oauthCode := ctx.Request.FormValue("code")
|
||||||
|
if oauthCode == "" {
|
||||||
|
log.Debug("Invalid oauth code: ", oauthCode)
|
||||||
|
ctx.JSON(400, gin.H{"error": "invalid oauth code"})
|
||||||
|
return
|
||||||
|
}
|
||||||
switch provider {
|
switch provider {
|
||||||
case constants.AuthRecipeMethodGoogle:
|
case constants.AuthRecipeMethodGoogle:
|
||||||
user, err = processGoogleUserInfo(oauthCode)
|
user, err = processGoogleUserInfo(ctx, oauthCode)
|
||||||
case constants.AuthRecipeMethodGithub:
|
case constants.AuthRecipeMethodGithub:
|
||||||
user, err = processGithubUserInfo(oauthCode)
|
user, err = processGithubUserInfo(ctx, oauthCode)
|
||||||
case constants.AuthRecipeMethodFacebook:
|
case constants.AuthRecipeMethodFacebook:
|
||||||
user, err = processFacebookUserInfo(oauthCode)
|
user, err = processFacebookUserInfo(ctx, oauthCode)
|
||||||
case constants.AuthRecipeMethodLinkedIn:
|
case constants.AuthRecipeMethodLinkedIn:
|
||||||
user, err = processLinkedInUserInfo(oauthCode)
|
user, err = processLinkedInUserInfo(ctx, oauthCode)
|
||||||
case constants.AuthRecipeMethodApple:
|
case constants.AuthRecipeMethodApple:
|
||||||
user, err = processAppleUserInfo(oauthCode)
|
user, err = processAppleUserInfo(ctx, oauthCode)
|
||||||
|
case constants.AuthRecipeMethodDiscord:
|
||||||
|
user, err = processDiscordUserInfo(ctx, oauthCode)
|
||||||
case constants.AuthRecipeMethodTwitter:
|
case constants.AuthRecipeMethodTwitter:
|
||||||
user, err = processTwitterUserInfo(oauthCode, sessionState)
|
user, err = processTwitterUserInfo(ctx, oauthCode, sessionState)
|
||||||
case constants.AuthRecipeMethodMicrosoft:
|
case constants.AuthRecipeMethodMicrosoft:
|
||||||
user, err = processMicrosoftUserInfo(oauthCode)
|
user, err = processMicrosoftUserInfo(ctx, oauthCode)
|
||||||
|
case constants.AuthRecipeMethodTwitch:
|
||||||
|
user, err = processTwitchUserInfo(ctx, oauthCode)
|
||||||
default:
|
default:
|
||||||
log.Info("Invalid oauth provider")
|
log.Info("Invalid oauth provider")
|
||||||
err = fmt.Errorf(`invalid oauth provider`)
|
err = fmt.Errorf(`invalid oauth provider`)
|
||||||
@@ -83,7 +100,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
existingUser, err := db.Provider.GetUserByEmail(ctx, user.Email)
|
existingUser, err := db.Provider.GetUserByEmail(ctx, refs.StringValue(user.Email))
|
||||||
log := log.WithField("user", user.Email)
|
log := log.WithField("user", user.Email)
|
||||||
isSignUp := false
|
isSignUp := false
|
||||||
|
|
||||||
@@ -241,8 +258,9 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
expiresIn = 1
|
expiresIn = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token + "&nonce=" + nonce
|
// params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token + "&nonce=" + nonce
|
||||||
|
// Note: If OIDC breaks in the future, use the above params
|
||||||
|
params := "state=" + stateValue + "&nonce=" + nonce
|
||||||
if code != "" {
|
if code != "" {
|
||||||
params += "&code=" + code
|
params += "&code=" + code
|
||||||
}
|
}
|
||||||
@@ -260,6 +278,8 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
go func() {
|
go func() {
|
||||||
if isSignUp {
|
if isSignUp {
|
||||||
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, provider, user)
|
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, provider, user)
|
||||||
|
// User is also logged in with signup
|
||||||
|
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, provider, user)
|
||||||
} else {
|
} else {
|
||||||
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, provider, user)
|
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, provider, user)
|
||||||
}
|
}
|
||||||
@@ -279,13 +299,11 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func processGoogleUserInfo(code string) (*models.User, error) {
|
func processGoogleUserInfo(ctx context.Context, code string) (*models.User, error) {
|
||||||
var user *models.User
|
|
||||||
ctx := context.Background()
|
|
||||||
oauth2Token, err := oauth.OAuthProviders.GoogleConfig.Exchange(ctx, code)
|
oauth2Token, err := oauth.OAuthProviders.GoogleConfig.Exchange(ctx, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to exchange code for token: ", err)
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
return user, fmt.Errorf("invalid google exchange code: %s", err.Error())
|
return nil, fmt.Errorf("invalid google exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
verifier := oauth.OIDCProviders.GoogleOIDC.Verifier(&oidc.Config{ClientID: oauth.OAuthProviders.GoogleConfig.ClientID})
|
verifier := oauth.OIDCProviders.GoogleOIDC.Verifier(&oidc.Config{ClientID: oauth.OAuthProviders.GoogleConfig.ClientID})
|
||||||
|
|
||||||
@@ -293,36 +311,35 @@ func processGoogleUserInfo(code string) (*models.User, error) {
|
|||||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debug("Failed to extract ID Token from OAuth2 token")
|
log.Debug("Failed to extract ID Token from OAuth2 token")
|
||||||
return user, fmt.Errorf("unable to extract id_token")
|
return nil, fmt.Errorf("unable to extract id_token")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse and verify ID Token payload.
|
// Parse and verify ID Token payload.
|
||||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to verify ID Token: ", err)
|
log.Debug("Failed to verify ID Token: ", err)
|
||||||
return user, fmt.Errorf("unable to verify id_token: %s", err.Error())
|
return nil, fmt.Errorf("unable to verify id_token: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
user := &models.User{}
|
||||||
if err := idToken.Claims(&user); err != nil {
|
if err := idToken.Claims(&user); err != nil {
|
||||||
log.Debug("Failed to parse ID Token claims: ", err)
|
log.Debug("Failed to parse ID Token claims: ", err)
|
||||||
return user, fmt.Errorf("unable to extract claims")
|
return nil, fmt.Errorf("unable to extract claims")
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func processGithubUserInfo(code string) (*models.User, error) {
|
func processGithubUserInfo(ctx context.Context, code string) (*models.User, error) {
|
||||||
var user *models.User
|
oauth2Token, err := oauth.OAuthProviders.GithubConfig.Exchange(ctx, code)
|
||||||
oauth2Token, err := oauth.OAuthProviders.GithubConfig.Exchange(context.TODO(), code)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to exchange code for token: ", err)
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
return user, fmt.Errorf("invalid github exchange code: %s", err.Error())
|
return nil, fmt.Errorf("invalid github exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
req, err := http.NewRequest("GET", constants.GithubUserInfoURL, nil)
|
req, err := http.NewRequest("GET", constants.GithubUserInfoURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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 nil, fmt.Errorf("error creating github user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
req.Header.Set(
|
req.Header.Set(
|
||||||
"Authorization", fmt.Sprintf("token %s", oauth2Token.AccessToken),
|
"Authorization", fmt.Sprintf("token %s", oauth2Token.AccessToken),
|
||||||
@@ -331,18 +348,18 @@ func processGithubUserInfo(code string) (*models.User, error) {
|
|||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to request github user info: ", err)
|
log.Debug("Failed to request github user info: ", err)
|
||||||
return user, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := io.ReadAll(response.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to read github user info response body: ", err)
|
log.Debug("Failed to read github user info response body: ", err)
|
||||||
return user, fmt.Errorf("failed to read github response body: %s", err.Error())
|
return nil, fmt.Errorf("failed to read github response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
if response.StatusCode >= 400 {
|
if response.StatusCode >= 400 {
|
||||||
log.Debug("Failed to request github user info: ", string(body))
|
log.Debug("Failed to request github user info: ", string(body))
|
||||||
return user, fmt.Errorf("failed to request github user info: %s", string(body))
|
return nil, fmt.Errorf("failed to request github user info: %s", string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
userRawData := make(map[string]string)
|
userRawData := make(map[string]string)
|
||||||
@@ -371,7 +388,7 @@ func processGithubUserInfo(code string) (*models.User, error) {
|
|||||||
req, err := http.NewRequest(http.MethodGet, 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 nil, fmt.Errorf("error creating github user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
req.Header.Set(
|
req.Header.Set(
|
||||||
"Authorization", fmt.Sprintf("token %s", oauth2Token.AccessToken),
|
"Authorization", fmt.Sprintf("token %s", oauth2Token.AccessToken),
|
||||||
@@ -380,24 +397,25 @@ func processGithubUserInfo(code string) (*models.User, error) {
|
|||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to request github user email: ", err)
|
log.Debug("Failed to request github user email: ", err)
|
||||||
return user, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := io.ReadAll(response.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to read github user email response body: ", err)
|
log.Debug("Failed to read github user email response body: ", err)
|
||||||
return user, fmt.Errorf("failed to read github response body: %s", err.Error())
|
return nil, fmt.Errorf("failed to read github response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
if response.StatusCode >= 400 {
|
if response.StatusCode >= 400 {
|
||||||
log.Debug("Failed to request github user email: ", string(body))
|
log.Debug("Failed to request github user email: ", string(body))
|
||||||
return user, fmt.Errorf("failed to request github user info: %s", string(body))
|
return nil, fmt.Errorf("failed to request github user info: %s", string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
emailData := []GithubUserEmails{}
|
emailData := []GithubUserEmails{}
|
||||||
err = json.Unmarshal(body, &emailData)
|
err = json.Unmarshal(body, &emailData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to parse github user email: ", err)
|
log.Debug("Failed to parse github user email: ", err)
|
||||||
|
return nil, fmt.Errorf("failed to parse github user email: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, userEmail := range emailData {
|
for _, userEmail := range emailData {
|
||||||
@@ -408,45 +426,44 @@ func processGithubUserInfo(code string) (*models.User, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user = &models.User{
|
user := &models.User{
|
||||||
GivenName: &firstName,
|
GivenName: &firstName,
|
||||||
FamilyName: &lastName,
|
FamilyName: &lastName,
|
||||||
Picture: &picture,
|
Picture: &picture,
|
||||||
Email: email,
|
Email: &email,
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func processFacebookUserInfo(code string) (*models.User, error) {
|
func processFacebookUserInfo(ctx context.Context, code string) (*models.User, error) {
|
||||||
var user *models.User
|
oauth2Token, err := oauth.OAuthProviders.FacebookConfig.Exchange(ctx, code)
|
||||||
oauth2Token, err := oauth.OAuthProviders.FacebookConfig.Exchange(context.TODO(), code)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Invalid facebook exchange code: ", err)
|
log.Debug("Invalid facebook exchange code: ", err)
|
||||||
return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
return nil, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
req, err := http.NewRequest("GET", constants.FacebookUserInfoURL+oauth2Token.AccessToken, nil)
|
req, err := http.NewRequest("GET", constants.FacebookUserInfoURL+oauth2Token.AccessToken, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error creating facebook user info request: ", err)
|
log.Debug("Error creating facebook user info request: ", err)
|
||||||
return user, fmt.Errorf("error creating facebook user info request: %s", err.Error())
|
return nil, fmt.Errorf("error creating facebook user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to process facebook user: ", err)
|
log.Debug("Failed to process facebook user: ", err)
|
||||||
return user, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := io.ReadAll(response.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to read facebook response: ", err)
|
log.Debug("Failed to read facebook response: ", err)
|
||||||
return user, fmt.Errorf("failed to read facebook response body: %s", err.Error())
|
return nil, fmt.Errorf("failed to read facebook response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
if response.StatusCode >= 400 {
|
if response.StatusCode >= 400 {
|
||||||
log.Debug("Failed to request facebook user info: ", string(body))
|
log.Debug("Failed to request facebook user info: ", string(body))
|
||||||
return user, fmt.Errorf("failed to request facebook user info: %s", string(body))
|
return nil, fmt.Errorf("failed to request facebook user info: %s", string(body))
|
||||||
}
|
}
|
||||||
userRawData := make(map[string]interface{})
|
userRawData := make(map[string]interface{})
|
||||||
json.Unmarshal(body, &userRawData)
|
json.Unmarshal(body, &userRawData)
|
||||||
@@ -459,29 +476,28 @@ func processFacebookUserInfo(code string) (*models.User, error) {
|
|||||||
lastName := fmt.Sprintf("%v", userRawData["last_name"])
|
lastName := fmt.Sprintf("%v", userRawData["last_name"])
|
||||||
picture := fmt.Sprintf("%v", picDataObject["url"])
|
picture := fmt.Sprintf("%v", picDataObject["url"])
|
||||||
|
|
||||||
user = &models.User{
|
user := &models.User{
|
||||||
GivenName: &firstName,
|
GivenName: &firstName,
|
||||||
FamilyName: &lastName,
|
FamilyName: &lastName,
|
||||||
Picture: &picture,
|
Picture: &picture,
|
||||||
Email: email,
|
Email: &email,
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func processLinkedInUserInfo(code string) (*models.User, error) {
|
func processLinkedInUserInfo(ctx context.Context, code string) (*models.User, error) {
|
||||||
var user *models.User
|
oauth2Token, err := oauth.OAuthProviders.LinkedInConfig.Exchange(ctx, code)
|
||||||
oauth2Token, err := oauth.OAuthProviders.LinkedInConfig.Exchange(context.TODO(), code)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to exchange code for token: ", err)
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
return user, fmt.Errorf("invalid linkedin exchange code: %s", err.Error())
|
return nil, fmt.Errorf("invalid linkedin exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
req, err := http.NewRequest("GET", constants.LinkedInUserInfoURL, nil)
|
req, err := http.NewRequest("GET", constants.LinkedInUserInfoURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to create linkedin user info request: ", err)
|
log.Debug("Failed to create linkedin user info request: ", err)
|
||||||
return user, fmt.Errorf("error creating linkedin user info request: %s", err.Error())
|
return nil, fmt.Errorf("error creating linkedin user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
req.Header = http.Header{
|
req.Header = http.Header{
|
||||||
"Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)},
|
"Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)},
|
||||||
@@ -490,19 +506,19 @@ func processLinkedInUserInfo(code string) (*models.User, error) {
|
|||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to request linkedin user info: ", err)
|
log.Debug("Failed to request linkedin user info: ", err)
|
||||||
return user, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := io.ReadAll(response.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to read linkedin user info response body: ", err)
|
log.Debug("Failed to read linkedin user info response body: ", err)
|
||||||
return user, fmt.Errorf("failed to read linkedin response body: %s", err.Error())
|
return nil, fmt.Errorf("failed to read linkedin response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.StatusCode >= 400 {
|
if response.StatusCode >= 400 {
|
||||||
log.Debug("Failed to request linkedin user info: ", string(body))
|
log.Debug("Failed to request linkedin user info: ", string(body))
|
||||||
return user, fmt.Errorf("failed to request linkedin user info: %s", string(body))
|
return nil, fmt.Errorf("failed to request linkedin user info: %s", string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
userRawData := make(map[string]interface{})
|
userRawData := make(map[string]interface{})
|
||||||
@@ -511,7 +527,7 @@ func processLinkedInUserInfo(code string) (*models.User, error) {
|
|||||||
req, err = http.NewRequest("GET", constants.LinkedInEmailURL, nil)
|
req, err = http.NewRequest("GET", constants.LinkedInEmailURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to create linkedin email info request: ", err)
|
log.Debug("Failed to create linkedin email info request: ", err)
|
||||||
return user, fmt.Errorf("error creating linkedin user info request: %s", err.Error())
|
return nil, fmt.Errorf("error creating linkedin user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
req.Header = http.Header{
|
req.Header = http.Header{
|
||||||
"Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)},
|
"Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)},
|
||||||
@@ -520,18 +536,18 @@ func processLinkedInUserInfo(code string) (*models.User, error) {
|
|||||||
response, err = client.Do(req)
|
response, err = client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to request linkedin email info: ", err)
|
log.Debug("Failed to request linkedin email info: ", err)
|
||||||
return user, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err = io.ReadAll(response.Body)
|
body, err = io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to read linkedin email info response body: ", err)
|
log.Debug("Failed to read linkedin email info response body: ", err)
|
||||||
return user, fmt.Errorf("failed to read linkedin email response body: %s", err.Error())
|
return nil, fmt.Errorf("failed to read linkedin email response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
if response.StatusCode >= 400 {
|
if response.StatusCode >= 400 {
|
||||||
log.Debug("Failed to request linkedin user info: ", string(body))
|
log.Debug("Failed to request linkedin user info: ", string(body))
|
||||||
return user, fmt.Errorf("failed to request linkedin user info: %s", string(body))
|
return nil, fmt.Errorf("failed to request linkedin user info: %s", string(body))
|
||||||
}
|
}
|
||||||
emailRawData := make(map[string]interface{})
|
emailRawData := make(map[string]interface{})
|
||||||
json.Unmarshal(body, &emailRawData)
|
json.Unmarshal(body, &emailRawData)
|
||||||
@@ -541,19 +557,19 @@ func processLinkedInUserInfo(code string) (*models.User, error) {
|
|||||||
profilePicture := userRawData["profilePicture"].(map[string]interface{})["displayImage~"].(map[string]interface{})["elements"].([]interface{})[0].(map[string]interface{})["identifiers"].([]interface{})[0].(map[string]interface{})["identifier"].(string)
|
profilePicture := userRawData["profilePicture"].(map[string]interface{})["displayImage~"].(map[string]interface{})["elements"].([]interface{})[0].(map[string]interface{})["identifiers"].([]interface{})[0].(map[string]interface{})["identifier"].(string)
|
||||||
emailAddress := emailRawData["elements"].([]interface{})[0].(map[string]interface{})["handle~"].(map[string]interface{})["emailAddress"].(string)
|
emailAddress := emailRawData["elements"].([]interface{})[0].(map[string]interface{})["handle~"].(map[string]interface{})["emailAddress"].(string)
|
||||||
|
|
||||||
user = &models.User{
|
user := &models.User{
|
||||||
GivenName: &firstName,
|
GivenName: &firstName,
|
||||||
FamilyName: &lastName,
|
FamilyName: &lastName,
|
||||||
Picture: &profilePicture,
|
Picture: &profilePicture,
|
||||||
Email: emailAddress,
|
Email: &emailAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func processAppleUserInfo(code string) (*models.User, error) {
|
func processAppleUserInfo(ctx context.Context, code string) (*models.User, error) {
|
||||||
var user *models.User
|
var user = &models.User{}
|
||||||
oauth2Token, err := oauth.OAuthProviders.AppleConfig.Exchange(context.TODO(), code)
|
oauth2Token, err := oauth.OAuthProviders.AppleConfig.Exchange(ctx, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to exchange code for token: ", err)
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
return user, fmt.Errorf("invalid apple exchange code: %s", err.Error())
|
return user, fmt.Errorf("invalid apple exchange code: %s", err.Error())
|
||||||
@@ -580,12 +596,12 @@ func processAppleUserInfo(code string) (*models.User, error) {
|
|||||||
log.Debug("Failed to unmarshal claims data: ", err)
|
log.Debug("Failed to unmarshal claims data: ", err)
|
||||||
return user, fmt.Errorf("failed to unmarshal claims data: %s", err.Error())
|
return user, fmt.Errorf("failed to unmarshal claims data: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
if val, ok := claims["email"]; !ok || val == nil {
|
||||||
if val, ok := claims["email"]; !ok {
|
|
||||||
log.Debug("Failed to extract email from claims.")
|
log.Debug("Failed to extract email from claims.")
|
||||||
return user, fmt.Errorf("unable to extract email, please check the scopes enabled for your app. It needs `email`, `name` scopes")
|
return user, fmt.Errorf("unable to extract email, please check the scopes enabled for your app. It needs `email`, `name` scopes")
|
||||||
} else {
|
} else {
|
||||||
user.Email = val.(string)
|
email := val.(string)
|
||||||
|
user.Email = &email
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := claims["name"]; ok {
|
if val, ok := claims["name"]; ok {
|
||||||
@@ -604,19 +620,83 @@ func processAppleUserInfo(code string) (*models.User, error) {
|
|||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func processTwitterUserInfo(code, verifier string) (*models.User, error) {
|
func processDiscordUserInfo(ctx context.Context, code string) (*models.User, error) {
|
||||||
var user *models.User
|
oauth2Token, err := oauth.OAuthProviders.DiscordConfig.Exchange(ctx, code)
|
||||||
oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(context.TODO(), code, oauth2.SetAuthURLParam("code_verifier", verifier))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to exchange code for token: ", err)
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
return user, fmt.Errorf("invalid twitter exchange code: %s", err.Error())
|
return nil, fmt.Errorf("invalid discord exchange code: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
client := http.Client{}
|
||||||
|
req, err := http.NewRequest("GET", constants.DiscordUserInfoURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to create Discord user info request: ", err)
|
||||||
|
return nil, fmt.Errorf("error creating Discord 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 Discord user info: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to read Discord user info response body: ", err)
|
||||||
|
return nil, fmt.Errorf("failed to read Discord response body: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode >= 400 {
|
||||||
|
log.Debug("Failed to request Discord user info: ", string(body))
|
||||||
|
return nil, fmt.Errorf("failed to request Discord user info: %s", string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the response body into a map
|
||||||
|
responseRawData := make(map[string]interface{})
|
||||||
|
if err := json.Unmarshal(body, &responseRawData); err != nil {
|
||||||
|
log.Debug("Failed to unmarshal Discord response: ", err)
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal Discord response: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safely extract the user data
|
||||||
|
userRawData, ok := responseRawData["user"].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
log.Debug("User data is not in expected format or missing in response")
|
||||||
|
return nil, fmt.Errorf("user data is not in expected format or missing in response")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the username
|
||||||
|
firstName, ok := userRawData["username"].(string)
|
||||||
|
if !ok {
|
||||||
|
log.Debug("Username is not in expected format or missing in user data")
|
||||||
|
return nil, fmt.Errorf("username is not in expected format or missing in user data")
|
||||||
|
}
|
||||||
|
profilePicture := fmt.Sprintf("https://cdn.discordapp.com/avatars/%s/%s.png", userRawData["id"].(string), userRawData["avatar"].(string))
|
||||||
|
|
||||||
|
user := &models.User{
|
||||||
|
GivenName: &firstName,
|
||||||
|
Picture: &profilePicture,
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processTwitterUserInfo(ctx context.Context, code, verifier string) (*models.User, error) {
|
||||||
|
oauth2Token, err := oauth.OAuthProviders.TwitterConfig.Exchange(ctx, code, oauth2.SetAuthURLParam("code_verifier", verifier))
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
|
return nil, fmt.Errorf("invalid twitter exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
req, err := http.NewRequest("GET", constants.TwitterUserInfoURL, nil)
|
req, err := http.NewRequest("GET", constants.TwitterUserInfoURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to create Twitter user info request: ", err)
|
log.Debug("Failed to create Twitter user info request: ", err)
|
||||||
return user, fmt.Errorf("error creating Twitter user info request: %s", err.Error())
|
return nil, fmt.Errorf("error creating Twitter user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
req.Header = http.Header{
|
req.Header = http.Header{
|
||||||
"Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)},
|
"Authorization": []string{fmt.Sprintf("Bearer %s", oauth2Token.AccessToken)},
|
||||||
@@ -625,19 +705,19 @@ func processTwitterUserInfo(code, verifier string) (*models.User, error) {
|
|||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to request Twitter user info: ", err)
|
log.Debug("Failed to request Twitter user info: ", err)
|
||||||
return user, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := io.ReadAll(response.Body)
|
body, err := io.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to read Twitter user info response body: ", err)
|
log.Debug("Failed to read Twitter user info response body: ", err)
|
||||||
return user, fmt.Errorf("failed to read Twitter response body: %s", err.Error())
|
return nil, fmt.Errorf("failed to read Twitter response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.StatusCode >= 400 {
|
if response.StatusCode >= 400 {
|
||||||
log.Debug("Failed to request Twitter user info: ", string(body))
|
log.Debug("Failed to request Twitter user info: ", string(body))
|
||||||
return user, fmt.Errorf("failed to request Twitter user info: %s", string(body))
|
return nil, fmt.Errorf("failed to request Twitter user info: %s", string(body))
|
||||||
}
|
}
|
||||||
|
|
||||||
responseRawData := make(map[string]interface{})
|
responseRawData := make(map[string]interface{})
|
||||||
@@ -661,7 +741,7 @@ func processTwitterUserInfo(code, verifier string) (*models.User, error) {
|
|||||||
nickname := userRawData["username"].(string)
|
nickname := userRawData["username"].(string)
|
||||||
profilePicture := userRawData["profile_image_url"].(string)
|
profilePicture := userRawData["profile_image_url"].(string)
|
||||||
|
|
||||||
user = &models.User{
|
user := &models.User{
|
||||||
GivenName: &firstName,
|
GivenName: &firstName,
|
||||||
FamilyName: &lastName,
|
FamilyName: &lastName,
|
||||||
Picture: &profilePicture,
|
Picture: &profilePicture,
|
||||||
@@ -672,34 +752,68 @@ func processTwitterUserInfo(code, verifier string) (*models.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process microsoft user information
|
// process microsoft user information
|
||||||
func processMicrosoftUserInfo(code string) (*models.User, error) {
|
func processMicrosoftUserInfo(ctx context.Context, code string) (*models.User, error) {
|
||||||
var user *models.User
|
|
||||||
ctx := context.Background()
|
|
||||||
oauth2Token, err := oauth.OAuthProviders.MicrosoftConfig.Exchange(ctx, code)
|
oauth2Token, err := oauth.OAuthProviders.MicrosoftConfig.Exchange(ctx, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to exchange code for token: ", err)
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
return user, fmt.Errorf("invalid google exchange code: %s", err.Error())
|
return nil, fmt.Errorf("invalid microsoft exchange code: %s", err.Error())
|
||||||
|
}
|
||||||
|
// we need to skip issuer check because for common tenant it will return internal issuer which does not match
|
||||||
|
verifier := oauth.OIDCProviders.MicrosoftOIDC.Verifier(&oidc.Config{
|
||||||
|
ClientID: oauth.OAuthProviders.MicrosoftConfig.ClientID,
|
||||||
|
SkipIssuerCheck: true,
|
||||||
|
})
|
||||||
|
// Extract the ID Token from OAuth2 token.
|
||||||
|
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||||
|
if !ok {
|
||||||
|
log.Debug("Failed to extract ID Token from OAuth2 token")
|
||||||
|
return nil, fmt.Errorf("unable to extract id_token")
|
||||||
|
}
|
||||||
|
// Parse and verify ID Token payload.
|
||||||
|
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to verify ID Token: ", err)
|
||||||
|
return nil, fmt.Errorf("unable to verify id_token: %s", err.Error())
|
||||||
|
}
|
||||||
|
user := &models.User{}
|
||||||
|
if err := idToken.Claims(&user); err != nil {
|
||||||
|
log.Debug("Failed to parse ID Token claims: ", err)
|
||||||
|
return nil, fmt.Errorf("unable to extract claims")
|
||||||
}
|
}
|
||||||
|
|
||||||
verifier := oauth.OIDCProviders.MicrosoftOIDC.Verifier(&oidc.Config{ClientID: oauth.OAuthProviders.MicrosoftConfig.ClientID})
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// process twitch user information
|
||||||
|
func processTwitchUserInfo(ctx context.Context, code string) (*models.User, error) {
|
||||||
|
oauth2Token, err := oauth.OAuthProviders.TwitchConfig.Exchange(ctx, code)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to exchange code for token: ", err)
|
||||||
|
return nil, fmt.Errorf("invalid twitch exchange code: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// Extract the ID Token from OAuth2 token.
|
// Extract the ID Token from OAuth2 token.
|
||||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Debug("Failed to extract ID Token from OAuth2 token")
|
log.Debug("Failed to extract ID Token from OAuth2 token")
|
||||||
return user, fmt.Errorf("unable to extract id_token")
|
return nil, fmt.Errorf("unable to extract id_token")
|
||||||
}
|
}
|
||||||
|
verifier := oauth.OIDCProviders.TwitchOIDC.Verifier(&oidc.Config{
|
||||||
|
ClientID: oauth.OAuthProviders.TwitchConfig.ClientID,
|
||||||
|
SkipIssuerCheck: true,
|
||||||
|
})
|
||||||
|
|
||||||
// Parse and verify ID Token payload.
|
// Parse and verify ID Token payload.
|
||||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
idToken, err := verifier.Verify(ctx, rawIDToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to verify ID Token: ", err)
|
log.Debug("Failed to verify ID Token: ", err)
|
||||||
return user, fmt.Errorf("unable to verify id_token: %s", err.Error())
|
return nil, fmt.Errorf("unable to verify id_token: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user := &models.User{}
|
||||||
if err := idToken.Claims(&user); err != nil {
|
if err := idToken.Claims(&user); err != nil {
|
||||||
log.Debug("Failed to parse ID Token claims: ", err)
|
log.Debug("Failed to parse ID Token claims: ", err)
|
||||||
return user, fmt.Errorf("unable to extract claims")
|
return nil, fmt.Errorf("unable to extract claims")
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
|
@@ -4,10 +4,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
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/memorystore"
|
||||||
"github.com/authorizerdev/authorizer/server/oauth"
|
"github.com/authorizerdev/authorizer/server/oauth"
|
||||||
@@ -190,6 +192,24 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
oauth.OAuthProviders.TwitterConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodTwitter
|
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"))
|
url := oauth.OAuthProviders.TwitterConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("code_challenge", challenge), oauth2.SetAuthURLParam("code_challenge_method", "S256"))
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
|
|
||||||
|
case constants.AuthRecipeMethodDiscord:
|
||||||
|
if oauth.OAuthProviders.DiscordConfig == nil {
|
||||||
|
log.Debug("Discord OAuth provider is not configured")
|
||||||
|
isProviderConfigured = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodDiscord)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error setting state: ", err)
|
||||||
|
c.JSON(500, gin.H{
|
||||||
|
"error": "internal server error",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oauth.OAuthProviders.DiscordConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodDiscord
|
||||||
|
url := oauth.OAuthProviders.DiscordConfig.AuthCodeURL(oauthStateString)
|
||||||
|
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")
|
||||||
@@ -227,6 +247,24 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
oauth.OAuthProviders.MicrosoftConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodMicrosoft
|
oauth.OAuthProviders.MicrosoftConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodMicrosoft
|
||||||
url := oauth.OAuthProviders.MicrosoftConfig.AuthCodeURL(oauthStateString)
|
url := oauth.OAuthProviders.MicrosoftConfig.AuthCodeURL(oauthStateString)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, url)
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
|
case constants.AuthRecipeMethodTwitch:
|
||||||
|
if oauth.OAuthProviders.TwitchConfig == nil {
|
||||||
|
log.Debug("Twitch OAuth provider is not configured")
|
||||||
|
isProviderConfigured = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodTwitch)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error setting state: ", err)
|
||||||
|
c.JSON(500, gin.H{
|
||||||
|
"error": "internal server error",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// during the init of OAuthProvider authorizer url might be empty
|
||||||
|
oauth.OAuthProviders.TwitchConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodTwitch
|
||||||
|
url := oauth.OAuthProviders.TwitchConfig.AuthCodeURL(oauthStateString)
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, url)
|
||||||
default:
|
default:
|
||||||
log.Debug("Invalid oauth provider: ", provider)
|
log.Debug("Invalid oauth provider: ", provider)
|
||||||
c.JSON(422, gin.H{
|
c.JSON(422, gin.H{
|
||||||
|
@@ -24,7 +24,7 @@ func OpenIDConfigurationHandler() gin.HandlerFunc {
|
|||||||
"response_types_supported": []string{"code", "token", "id_token"},
|
"response_types_supported": []string{"code", "token", "id_token"},
|
||||||
"scopes_supported": []string{"openid", "email", "profile"},
|
"scopes_supported": []string{"openid", "email", "profile"},
|
||||||
"response_modes_supported": []string{"query", "fragment", "form_post", "web_message"},
|
"response_modes_supported": []string{"query", "fragment", "form_post", "web_message"},
|
||||||
"subject_types_supported": "public",
|
"subject_types_supported": []string{"public"},
|
||||||
"id_token_signing_alg_values_supported": []string{jwtType},
|
"id_token_signing_alg_values_supported": []string{jwtType},
|
||||||
"claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "role", "gender", "birthdate", "phone_number", "phone_number_verified", "nonce", "updated_at", "created_at", "revoked_timestamp", "login_method", "signup_methods", "token_type"},
|
"claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "role", "gender", "birthdate", "phone_number", "phone_number_verified", "nonce", "updated_at", "created_at", "revoked_timestamp", "login_method", "signup_methods", "token_type"},
|
||||||
})
|
})
|
||||||
|
@@ -1,15 +1,44 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql/playground"
|
"github.com/99designs/gqlgen/graphql/playground"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PlaygroundHandler is the handler for the /playground route
|
// PlaygroundHandler is the handler for the /playground route
|
||||||
func PlaygroundHandler() gin.HandlerFunc {
|
func PlaygroundHandler() gin.HandlerFunc {
|
||||||
h := playground.Handler("GraphQL", "/graphql")
|
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
var h http.HandlerFunc
|
||||||
|
|
||||||
|
disablePlayground, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePlayGround)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("error while getting disable playground value")
|
||||||
|
disablePlayground = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// if env set to false, then check if logged in as super admin, if logged in then return graphql else 401 error
|
||||||
|
// if env set to true, then disabled the playground with 404 error
|
||||||
|
if !disablePlayground {
|
||||||
|
if token.IsSuperAdmin(c) {
|
||||||
|
h = playground.Handler("GraphQL", "/graphql")
|
||||||
|
} else {
|
||||||
|
log.Debug("not logged in as super admin")
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "not logged in as super admin"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debug("playground is disabled")
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "playground is disabled"})
|
||||||
|
return
|
||||||
|
}
|
||||||
h.ServeHTTP(c.Writer, c.Request)
|
h.ServeHTTP(c.Writer, c.Request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -105,7 +105,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
if codeVerifier == "" && clientSecret == "" {
|
if codeVerifier == "" && clientSecret == "" {
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
"error": "invalid_dat",
|
"error": "invalid_data",
|
||||||
"error_description": "The code verifier or client secret is required",
|
"error_description": "The code verifier or client secret is required",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -263,12 +263,10 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
"roles": roles,
|
"roles": roles,
|
||||||
"expires_in": expiresIn,
|
"expires_in": expiresIn,
|
||||||
}
|
}
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res["refresh_token"] = authToken.RefreshToken.Token
|
res["refresh_token"] = authToken.RefreshToken.Token
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.JSON(http.StatusOK, res)
|
gc.JSON(http.StatusOK, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,6 @@ func UserInfoHandler() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
claims, err := token.ValidateAccessToken(gc, accessToken)
|
claims, err := token.ValidateAccessToken(gc, accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error validating access token: ", err)
|
log.Debug("Error validating access token: ", err)
|
||||||
@@ -30,7 +29,6 @@ func UserInfoHandler() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userID := claims["sub"].(string)
|
userID := claims["sub"].(string)
|
||||||
user, err := db.Provider.GetUserByID(gc, userID)
|
user, err := db.Provider.GetUserByID(gc, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -74,7 +74,13 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
user.EmailVerifiedAt = &now
|
user.EmailVerifiedAt = &now
|
||||||
isSignUp = true
|
isSignUp = true
|
||||||
db.Provider.UpdateUser(c, user)
|
user, err = db.Provider.UpdateUser(c, user)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error updating user: ", err)
|
||||||
|
errorRes["error"] = err.Error()
|
||||||
|
utils.HandleRedirectORJsonResponse(c, http.StatusBadRequest, errorRes, generateRedirectURL(redirectURL, errorRes))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// delete from verification table
|
// delete from verification table
|
||||||
db.Provider.DeleteVerificationRequest(c, verificationRequest)
|
db.Provider.DeleteVerificationRequest(c, verificationRequest)
|
||||||
@@ -175,6 +181,8 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
go func() {
|
go func() {
|
||||||
if isSignUp {
|
if isSignUp {
|
||||||
utils.RegisterEvent(c, constants.UserSignUpWebhookEvent, loginMethod, user)
|
utils.RegisterEvent(c, constants.UserSignUpWebhookEvent, loginMethod, user)
|
||||||
|
// User is also logged in with signup
|
||||||
|
utils.RegisterEvent(c, constants.UserLoginWebhookEvent, loginMethod, user)
|
||||||
} else {
|
} else {
|
||||||
utils.RegisterEvent(c, constants.UserLoginWebhookEvent, loginMethod, user)
|
utils.RegisterEvent(c, constants.UserLoginWebhookEvent, loginMethod, user)
|
||||||
}
|
}
|
||||||
|
@@ -7,22 +7,11 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogUTCFormatter hels in setting UTC time format for the logs
|
|
||||||
type LogUTCFormatter struct {
|
|
||||||
log.Formatter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format helps fomratting time to UTC
|
|
||||||
func (u LogUTCFormatter) Format(e *log.Entry) ([]byte, error) {
|
|
||||||
e.Time = e.Time.UTC()
|
|
||||||
return u.Formatter.Format(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitLog(cliLogLevel string) *log.Logger {
|
func InitLog(cliLogLevel string) *log.Logger {
|
||||||
|
|
||||||
// log instance for gin server
|
// log instance for gin server
|
||||||
log := logrus.New()
|
log := logrus.New()
|
||||||
log.SetFormatter(LogUTCFormatter{&logrus.JSONFormatter{}})
|
log.SetFormatter(&LogTextFormatter{})
|
||||||
|
|
||||||
if cliLogLevel == "" {
|
if cliLogLevel == "" {
|
||||||
cliLogLevel = os.Getenv("LOG_LEVEL")
|
cliLogLevel = os.Getenv("LOG_LEVEL")
|
||||||
|
18
server/logs/text.go
Normal file
18
server/logs/text.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogTextFormatter is a custom log formatter for text output
|
||||||
|
type LogTextFormatter struct {
|
||||||
|
logrus.Formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format helps fomratting time to UTC
|
||||||
|
func (u LogTextFormatter) Format(e *logrus.Entry) ([]byte, error) {
|
||||||
|
return []byte(fmt.Sprintf("[%s] %s", strings.ToUpper(e.Level.String()), e.Message)), nil
|
||||||
|
}
|
16
server/logs/utc.go
Normal file
16
server/logs/utc.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package logs
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogUTCFormatter hels in setting UTC time format for the logs
|
||||||
|
type LogUTCFormatter struct {
|
||||||
|
log.Formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format helps fomratting time to UTC
|
||||||
|
func (u LogUTCFormatter) Format(e *log.Entry) ([]byte, error) {
|
||||||
|
e.Time = e.Time.UTC()
|
||||||
|
return u.Formatter.Format(e)
|
||||||
|
}
|
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"github.com/authorizerdev/authorizer/server/authenticators"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/cli"
|
"github.com/authorizerdev/authorizer/server/cli"
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
@@ -27,7 +28,7 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// global log level
|
// global log level
|
||||||
logrus.SetFormatter(logs.LogUTCFormatter{&logrus.JSONFormatter{}})
|
logrus.SetFormatter(&logs.LogTextFormatter{})
|
||||||
|
|
||||||
constants.VERSION = VERSION
|
constants.VERSION = VERSION
|
||||||
|
|
||||||
@@ -70,6 +71,11 @@ func main() {
|
|||||||
log.Fatalln("Error while initializing oauth: ", err)
|
log.Fatalln("Error while initializing oauth: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = authenticators.InitTOTPStore()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Error while initializing authenticator: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
router := routes.InitRouter(log)
|
router := routes.InitRouter(log)
|
||||||
log.Info("Starting Authorizer: ", VERSION)
|
log.Info("Starting Authorizer: ", VERSION)
|
||||||
port, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyPort)
|
port, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyPort)
|
||||||
|
@@ -36,8 +36,11 @@ func InitMemStore() error {
|
|||||||
constants.EnvKeyIsSMSServiceEnabled: false,
|
constants.EnvKeyIsSMSServiceEnabled: false,
|
||||||
constants.EnvKeyEnforceMultiFactorAuthentication: false,
|
constants.EnvKeyEnforceMultiFactorAuthentication: false,
|
||||||
constants.EnvKeyDisableMultiFactorAuthentication: false,
|
constants.EnvKeyDisableMultiFactorAuthentication: false,
|
||||||
|
constants.EnvKeyDisableTOTPLogin: false,
|
||||||
constants.EnvKeyAppCookieSecure: true,
|
constants.EnvKeyAppCookieSecure: true,
|
||||||
constants.EnvKeyAdminCookieSecure: true,
|
constants.EnvKeyAdminCookieSecure: true,
|
||||||
|
constants.EnvKeyDisablePlayGround: true,
|
||||||
|
constants.EnvKeyDisableMailOTPLogin: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
requiredEnvs := RequiredEnvStoreObj.GetRequiredEnv()
|
requiredEnvs := RequiredEnvStoreObj.GetRequiredEnv()
|
||||||
|
@@ -7,18 +7,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type provider struct {
|
type provider struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
sessionStore *stores.SessionStore
|
sessionStore *stores.SessionStore
|
||||||
stateStore *stores.StateStore
|
mfasessionStore *stores.SessionStore
|
||||||
envStore *stores.EnvStore
|
stateStore *stores.StateStore
|
||||||
|
envStore *stores.EnvStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInMemoryStore returns a new in-memory store.
|
// NewInMemoryStore returns a new in-memory store.
|
||||||
func NewInMemoryProvider() (*provider, error) {
|
func NewInMemoryProvider() (*provider, error) {
|
||||||
return &provider{
|
return &provider{
|
||||||
mutex: sync.Mutex{},
|
mutex: sync.Mutex{},
|
||||||
envStore: stores.NewEnvStore(),
|
envStore: stores.NewEnvStore(),
|
||||||
sessionStore: stores.NewSessionStore(),
|
sessionStore: stores.NewSessionStore(),
|
||||||
stateStore: stores.NewStateStore(),
|
mfasessionStore: stores.NewSessionStore(),
|
||||||
|
stateStore: stores.NewStateStore(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,27 @@ func (c *provider) DeleteSessionForNamespace(namespace string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMfaSession sets the mfa session with key and value of userId
|
||||||
|
func (c *provider) SetMfaSession(userId, key string, expiration int64) error {
|
||||||
|
c.mfasessionStore.Set(userId, key, userId, expiration)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMfaSession returns value of given mfa session
|
||||||
|
func (c *provider) GetMfaSession(userId, key string) (string, error) {
|
||||||
|
val := c.mfasessionStore.Get(userId, key)
|
||||||
|
if val == "" {
|
||||||
|
return "", fmt.Errorf("Not found")
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMfaSession deletes given mfa session from in-memory store.
|
||||||
|
func (c *provider) DeleteMfaSession(userId, key string) error {
|
||||||
|
c.mfasessionStore.Remove(userId, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetState sets the state in the in-memory store.
|
// SetState sets the state in the in-memory store.
|
||||||
func (c *provider) SetState(key, state string) error {
|
func (c *provider) SetState(key, state string) error {
|
||||||
if os.Getenv("ENV") != constants.TestEnv {
|
if os.Getenv("ENV") != constants.TestEnv {
|
||||||
|
@@ -112,4 +112,15 @@ func ProviderTests(t *testing.T, p Provider) {
|
|||||||
key, err = p.GetUserSession("auth_provider1:124", "access_token_key")
|
key, err = p.GetUserSession("auth_provider1:124", "access_token_key")
|
||||||
assert.Empty(t, key)
|
assert.Empty(t, key)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
err = p.SetMfaSession("auth_provider:123", "session123", time.Now().Add(60*time.Second).Unix())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
key, err = p.GetMfaSession("auth_provider:123", "session123")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "auth_provider:123", key)
|
||||||
|
err = p.DeleteMfaSession("auth_provider:123", "session123")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
key, err = p.GetMfaSession("auth_provider:123", "session123")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, key)
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,12 @@ type Provider interface {
|
|||||||
DeleteAllUserSessions(userId string) error
|
DeleteAllUserSessions(userId string) error
|
||||||
// DeleteSessionForNamespace deletes the session for a given namespace
|
// DeleteSessionForNamespace deletes the session for a given namespace
|
||||||
DeleteSessionForNamespace(namespace string) error
|
DeleteSessionForNamespace(namespace string) error
|
||||||
|
// SetMfaSession sets the mfa session with key and value of userId
|
||||||
|
SetMfaSession(userId, key string, expiration int64) error
|
||||||
|
// GetMfaSession returns value of given mfa session
|
||||||
|
GetMfaSession(userId, key string) (string, error)
|
||||||
|
// DeleteMfaSession deletes given mfa session from in-memory store.
|
||||||
|
DeleteMfaSession(userId, key string) error
|
||||||
|
|
||||||
// SetState sets the login state (key, value form) in the session store
|
// SetState sets the login state (key, value form) in the session store
|
||||||
SetState(key, state string) error
|
SetState(key, state string) error
|
||||||
|
@@ -16,6 +16,8 @@ var (
|
|||||||
envStorePrefix = "authorizer_env"
|
envStorePrefix = "authorizer_env"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const mfaSessionPrefix = "mfa_sess_"
|
||||||
|
|
||||||
// SetUserSession sets the user session for given user identifier in form recipe:user_id
|
// SetUserSession sets the user session for given user identifier in form recipe:user_id
|
||||||
func (c *provider) SetUserSession(userId, key, token string, expiration int64) error {
|
func (c *provider) SetUserSession(userId, key, token string, expiration int64) error {
|
||||||
currentTime := time.Now()
|
currentTime := time.Now()
|
||||||
@@ -91,6 +93,37 @@ func (c *provider) DeleteSessionForNamespace(namespace string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMfaSession sets the mfa session with key and value of userId
|
||||||
|
func (c *provider) SetMfaSession(userId, key string, expiration int64) error {
|
||||||
|
currentTime := time.Now()
|
||||||
|
expireTime := time.Unix(expiration, 0)
|
||||||
|
duration := expireTime.Sub(currentTime)
|
||||||
|
err := c.store.Set(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key), userId, duration).Err()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error saving user session to redis: ", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMfaSession returns value of given mfa session
|
||||||
|
func (c *provider) GetMfaSession(userId, key string) (string, error) {
|
||||||
|
data, err := c.store.Get(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key)).Result()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMfaSession deletes given mfa session from in-memory store.
|
||||||
|
func (c *provider) DeleteMfaSession(userId, key string) error {
|
||||||
|
if err := c.store.Del(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key)).Err(); err != nil {
|
||||||
|
log.Debug("Error deleting user session from redis: ", err)
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetState sets the state in redis store.
|
// SetState sets the state in redis store.
|
||||||
func (c *provider) SetState(key, value string) error {
|
func (c *provider) SetState(key, value string) error {
|
||||||
err := c.store.Set(c.ctx, stateStorePrefix+key, value, 0).Err()
|
err := c.store.Set(c.ctx, stateStorePrefix+key, value, 0).Err()
|
||||||
@@ -143,7 +176,7 @@ func (c *provider) GetEnvStore() (map[string]interface{}, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for key, value := range data {
|
for key, value := range data {
|
||||||
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyIsSMSServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure {
|
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyIsSMSServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure || key == constants.EnvKeyDisablePlayGround || key == constants.EnvKeyDisableTOTPLogin || key == constants.EnvKeyDisableMailOTPLogin {
|
||||||
boolValue, err := strconv.ParseBool(value)
|
boolValue, err := strconv.ParseBool(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@@ -38,7 +38,7 @@ type RequiredEnv struct {
|
|||||||
CouchbaseBucketRAMQuotaMB string `json:"COUCHBASE_BUCKET_RAM_QUOTA"`
|
CouchbaseBucketRAMQuotaMB string `json:"COUCHBASE_BUCKET_RAM_QUOTA"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequiredEnvObj is a simple in-memory store for sessions.
|
// RequiredEnvStore is a simple in-memory store for sessions.
|
||||||
type RequiredEnvStore struct {
|
type RequiredEnvStore struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
requiredEnv RequiredEnv
|
requiredEnv RequiredEnv
|
||||||
|
@@ -4,17 +4,25 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
"google.golang.org/appengine/log"
|
||||||
|
|
||||||
facebookOAuth2 "golang.org/x/oauth2/facebook"
|
facebookOAuth2 "golang.org/x/oauth2/facebook"
|
||||||
githubOAuth2 "golang.org/x/oauth2/github"
|
githubOAuth2 "golang.org/x/oauth2/github"
|
||||||
linkedInOAuth2 "golang.org/x/oauth2/linkedin"
|
linkedInOAuth2 "golang.org/x/oauth2/linkedin"
|
||||||
microsoftOAuth2 "golang.org/x/oauth2/microsoft"
|
microsoftOAuth2 "golang.org/x/oauth2/microsoft"
|
||||||
|
twitchOAuth2 "golang.org/x/oauth2/twitch"
|
||||||
|
|
||||||
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
microsoftCommonTenant = "common"
|
||||||
|
)
|
||||||
|
|
||||||
// OAuthProviders is a struct that contains reference all the OAuth providers
|
// OAuthProviders is a struct that contains reference all the OAuth providers
|
||||||
type OAuthProvider struct {
|
type OAuthProvider struct {
|
||||||
GoogleConfig *oauth2.Config
|
GoogleConfig *oauth2.Config
|
||||||
@@ -22,14 +30,17 @@ type OAuthProvider struct {
|
|||||||
FacebookConfig *oauth2.Config
|
FacebookConfig *oauth2.Config
|
||||||
LinkedInConfig *oauth2.Config
|
LinkedInConfig *oauth2.Config
|
||||||
AppleConfig *oauth2.Config
|
AppleConfig *oauth2.Config
|
||||||
|
DiscordConfig *oauth2.Config
|
||||||
TwitterConfig *oauth2.Config
|
TwitterConfig *oauth2.Config
|
||||||
MicrosoftConfig *oauth2.Config
|
MicrosoftConfig *oauth2.Config
|
||||||
|
TwitchConfig *oauth2.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// OIDCProviders is a struct that contains reference all the OpenID providers
|
// OIDCProviders is a struct that contains reference all the OpenID providers
|
||||||
type OIDCProvider struct {
|
type OIDCProvider struct {
|
||||||
GoogleOIDC *oidc.Provider
|
GoogleOIDC *oidc.Provider
|
||||||
MicrosoftOIDC *oidc.Provider
|
MicrosoftOIDC *oidc.Provider
|
||||||
|
TwitchOIDC *oidc.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -139,6 +150,27 @@ func InitOAuth() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
discordClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDiscordClientID)
|
||||||
|
if err != nil {
|
||||||
|
discordClientID = ""
|
||||||
|
}
|
||||||
|
discordClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDiscordClientSecret)
|
||||||
|
if err != nil {
|
||||||
|
discordClientSecret = ""
|
||||||
|
}
|
||||||
|
if discordClientID != "" && discordClientSecret != "" {
|
||||||
|
OAuthProviders.DiscordConfig = &oauth2.Config{
|
||||||
|
ClientID: discordClientID,
|
||||||
|
ClientSecret: discordClientSecret,
|
||||||
|
RedirectURL: "/oauth_callback/discord",
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: "https://discord.com/oauth2/authorize",
|
||||||
|
TokenURL: "https://discord.com/api/oauth2/token",
|
||||||
|
},
|
||||||
|
Scopes: []string{"identify", "email"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
twitterClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitterClientID)
|
twitterClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitterClientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
twitterClientID = ""
|
twitterClientID = ""
|
||||||
@@ -171,12 +203,16 @@ func InitOAuth() error {
|
|||||||
microsoftClientSecret = ""
|
microsoftClientSecret = ""
|
||||||
}
|
}
|
||||||
microsoftActiveDirTenantID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftActiveDirectoryTenantID)
|
microsoftActiveDirTenantID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyMicrosoftActiveDirectoryTenantID)
|
||||||
if err != nil {
|
if err != nil || microsoftActiveDirTenantID == "" {
|
||||||
microsoftActiveDirTenantID = "common"
|
microsoftActiveDirTenantID = microsoftCommonTenant
|
||||||
}
|
}
|
||||||
if microsoftClientID != "" && microsoftClientSecret != "" && microsoftActiveDirTenantID != "" {
|
if microsoftClientID != "" && microsoftClientSecret != "" {
|
||||||
|
if microsoftActiveDirTenantID == microsoftCommonTenant {
|
||||||
|
ctx = oidc.InsecureIssuerURLContext(ctx, fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", microsoftActiveDirTenantID))
|
||||||
|
}
|
||||||
p, err := oidc.NewProvider(ctx, fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", microsoftActiveDirTenantID))
|
p, err := oidc.NewProvider(ctx, fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", microsoftActiveDirTenantID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf(ctx, "Error while creating OIDC provider for Microsoft: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
OIDCProviders.MicrosoftOIDC = p
|
OIDCProviders.MicrosoftOIDC = p
|
||||||
@@ -189,5 +225,31 @@ func InitOAuth() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
twitchClientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitchClientID)
|
||||||
|
if err != nil {
|
||||||
|
twitchClientID = ""
|
||||||
|
}
|
||||||
|
twitchClientSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyTwitchClientSecret)
|
||||||
|
if err != nil {
|
||||||
|
twitchClientSecret = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if twitchClientID != "" && twitchClientSecret != "" {
|
||||||
|
p, err := oidc.NewProvider(ctx, "https://id.twitch.tv/oauth2")
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf(ctx, "Error while creating OIDC provider for Twitch: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
OIDCProviders.TwitchOIDC = p
|
||||||
|
OAuthProviders.TwitchConfig = &oauth2.Config{
|
||||||
|
ClientID: twitchClientID,
|
||||||
|
ClientSecret: twitchClientSecret,
|
||||||
|
RedirectURL: "/oauth_callback/twitch",
|
||||||
|
Endpoint: twitchOAuth2.Endpoint,
|
||||||
|
Scopes: []string{oidc.ScopeOpenID},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
52
server/resolvers/deactivate_account.go
Normal file
52
server/resolvers/deactivate_account.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package resolvers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeactivateAccountResolver is the resolver for the deactivate_account field.
|
||||||
|
func DeactivateAccountResolver(ctx context.Context) (*model.Response, error) {
|
||||||
|
var res *model.Response
|
||||||
|
gc, err := utils.GinContextFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get GinContext: ", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
tokenData, err := token.GetUserIDFromSessionOrAccessToken(gc)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed GetUserIDFromSessionOrAccessToken: ", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
log := log.WithFields(log.Fields{
|
||||||
|
"user_id": tokenData.UserID,
|
||||||
|
})
|
||||||
|
user, err := db.Provider.GetUserByID(ctx, tokenData.UserID)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get user by id: ", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
now := time.Now().Unix()
|
||||||
|
user.RevokedTimestamp = &now
|
||||||
|
user, err = db.Provider.UpdateUser(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to update user: ", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
memorystore.Provider.DeleteAllUserSessions(user.ID)
|
||||||
|
utils.RegisterEvent(ctx, constants.UserDeactivatedWebhookEvent, "", user)
|
||||||
|
}()
|
||||||
|
res = &model.Response{
|
||||||
|
Message: `user account deactivated successfully`,
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
|
"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"
|
||||||
)
|
)
|
||||||
@@ -51,28 +52,41 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
// delete otp for given email
|
// delete otp for given email
|
||||||
otp, err := db.Provider.GetOTPByEmail(ctx, user.Email)
|
otp, err := db.Provider.GetOTPByEmail(ctx, refs.StringValue(user.Email))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("No OTP found for email (%s): %v", user.Email, err)
|
log.Infof("No OTP found for email (%s): %v", user.Email, err)
|
||||||
// continue
|
// continue
|
||||||
} else {
|
} else {
|
||||||
err := db.Provider.DeleteOTP(ctx, otp)
|
err := db.Provider.DeleteOTP(ctx, otp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("Failed to delete otp for given email (%s): %v", user.Email, err)
|
log.Debugf("Failed to delete otp for given email (%s): %v", refs.StringValue(user.Email), err)
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete otp for given phone number
|
||||||
|
otp, err = db.Provider.GetOTPByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber))
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("No OTP found for email (%s): %v", refs.StringValue(user.Email), err)
|
||||||
|
// continue
|
||||||
|
} else {
|
||||||
|
err := db.Provider.DeleteOTP(ctx, otp)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Failed to delete otp for given phone (%s): %v", refs.StringValue(user.PhoneNumber), err)
|
||||||
// continue
|
// continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete verification requests for given email
|
// delete verification requests for given email
|
||||||
for _, vt := range constants.VerificationTypes {
|
for _, vt := range constants.VerificationTypes {
|
||||||
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, user.Email, vt)
|
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, refs.StringValue(user.Email), vt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("No verification verification request found for email: %s, verification_request_type: %s. %v", user.Email, vt, err)
|
log.Infof("No verification verification request found for email: %s, verification_request_type: %s. %v", refs.StringValue(user.Email), vt, err)
|
||||||
// continue
|
// continue
|
||||||
} else {
|
} else {
|
||||||
err := db.Provider.DeleteVerificationRequest(ctx, verificationRequest)
|
err := db.Provider.DeleteVerificationRequest(ctx, verificationRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("Failed to DeleteVerificationRequest for email: %s, verification_request_type: %s. %v", user.Email, vt, err)
|
log.Debugf("Failed to DeleteVerificationRequest for email: %s, verification_request_type: %s. %v", refs.StringValue(user.Email), vt, err)
|
||||||
// continue
|
// continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -149,6 +149,12 @@ 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.EnvKeyDiscordClientID]; ok {
|
||||||
|
res.DiscordClientID = refs.NewStringRef(val.(string))
|
||||||
|
}
|
||||||
|
if val, ok := store[constants.EnvKeyDiscordClientSecret]; ok {
|
||||||
|
res.DiscordClientSecret = refs.NewStringRef(val.(string))
|
||||||
|
}
|
||||||
if val, ok := store[constants.EnvKeyTwitterClientID]; ok {
|
if val, ok := store[constants.EnvKeyTwitterClientID]; ok {
|
||||||
res.TwitterClientID = refs.NewStringRef(val.(string))
|
res.TwitterClientID = refs.NewStringRef(val.(string))
|
||||||
}
|
}
|
||||||
@@ -164,7 +170,12 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
|
|||||||
if val, ok := store[constants.EnvKeyMicrosoftActiveDirectoryTenantID]; ok {
|
if val, ok := store[constants.EnvKeyMicrosoftActiveDirectoryTenantID]; ok {
|
||||||
res.MicrosoftActiveDirectoryTenantID = refs.NewStringRef(val.(string))
|
res.MicrosoftActiveDirectoryTenantID = refs.NewStringRef(val.(string))
|
||||||
}
|
}
|
||||||
|
if val, ok := store[constants.EnvKeyTwitchClientID]; ok {
|
||||||
|
res.TwitchClientID = refs.NewStringRef(val.(string))
|
||||||
|
}
|
||||||
|
if val, ok := store[constants.EnvKeyTwitchClientSecret]; ok {
|
||||||
|
res.TwitchClientSecret = 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))
|
||||||
}
|
}
|
||||||
@@ -202,6 +213,9 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
|
|||||||
res.DisableMultiFactorAuthentication = store[constants.EnvKeyDisableMultiFactorAuthentication].(bool)
|
res.DisableMultiFactorAuthentication = store[constants.EnvKeyDisableMultiFactorAuthentication].(bool)
|
||||||
res.AdminCookieSecure = store[constants.EnvKeyAdminCookieSecure].(bool)
|
res.AdminCookieSecure = store[constants.EnvKeyAdminCookieSecure].(bool)
|
||||||
res.AppCookieSecure = store[constants.EnvKeyAppCookieSecure].(bool)
|
res.AppCookieSecure = store[constants.EnvKeyAppCookieSecure].(bool)
|
||||||
|
res.DisablePlayground = store[constants.EnvKeyDisablePlayGround].(bool)
|
||||||
|
res.DisableMailOtpLogin = store[constants.EnvKeyDisableMailOTPLogin].(bool)
|
||||||
|
res.DisableTotpLogin = store[constants.EnvKeyDisableTOTPLogin].(bool)
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@@ -6,29 +6,29 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/email"
|
mailService "github.com/authorizerdev/authorizer/server/email"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
"github.com/authorizerdev/authorizer/server/parsers"
|
"github.com/authorizerdev/authorizer/server/parsers"
|
||||||
"github.com/authorizerdev/authorizer/server/refs"
|
"github.com/authorizerdev/authorizer/server/refs"
|
||||||
|
"github.com/authorizerdev/authorizer/server/smsproviders"
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ForgotPasswordResolver is a resolver for forgot password mutation
|
// ForgotPasswordResolver is a resolver for forgot password mutation
|
||||||
func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) {
|
func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error) {
|
||||||
var res *model.Response
|
|
||||||
|
|
||||||
gc, err := utils.GinContextFromContext(ctx)
|
gc, err := utils.GinContextFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to get GinContext: ", err)
|
log.Debug("Failed to get GinContext: ", err)
|
||||||
return res, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
|
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
|
||||||
@@ -36,74 +36,134 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||||||
log.Debug("Error getting basic auth disabled: ", err)
|
log.Debug("Error getting basic auth disabled: ", err)
|
||||||
isBasicAuthDisabled = true
|
isBasicAuthDisabled = true
|
||||||
}
|
}
|
||||||
if isBasicAuthDisabled {
|
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
|
||||||
log.Debug("Basic authentication is disabled")
|
|
||||||
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
|
||||||
}
|
|
||||||
params.Email = strings.ToLower(params.Email)
|
|
||||||
|
|
||||||
if !validators.IsValidEmail(params.Email) {
|
|
||||||
log.Debug("Invalid email address: ", params.Email)
|
|
||||||
return res, fmt.Errorf("invalid email")
|
|
||||||
}
|
|
||||||
|
|
||||||
log := log.WithFields(log.Fields{
|
|
||||||
"email": params.Email,
|
|
||||||
})
|
|
||||||
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("User not found: ", err)
|
log.Debug("Error getting email verification disabled: ", err)
|
||||||
return res, fmt.Errorf(`user with this email not found`)
|
isEmailVerificationDisabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error getting mobile basic auth disabled: ", err)
|
||||||
|
isMobileBasicAuthDisabled = true
|
||||||
|
}
|
||||||
|
isMobileVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error getting mobile verification disabled: ", err)
|
||||||
|
isMobileVerificationDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
email := refs.StringValue(params.Email)
|
||||||
|
phoneNumber := refs.StringValue(params.PhoneNumber)
|
||||||
|
if email == "" && phoneNumber == "" {
|
||||||
|
log.Debug("Email or phone number is required")
|
||||||
|
return nil, fmt.Errorf(`email or phone number is required`)
|
||||||
|
}
|
||||||
|
log := log.WithFields(log.Fields{
|
||||||
|
"email": refs.StringValue(params.Email),
|
||||||
|
"phone_number": refs.StringValue(params.PhoneNumber),
|
||||||
|
})
|
||||||
|
isEmailLogin := email != ""
|
||||||
|
isMobileLogin := phoneNumber != ""
|
||||||
|
if isBasicAuthDisabled && isEmailLogin && !isEmailVerificationDisabled {
|
||||||
|
log.Debug("Basic authentication is disabled.")
|
||||||
|
return nil, fmt.Errorf(`basic authentication is disabled for this instance`)
|
||||||
|
}
|
||||||
|
if isMobileBasicAuthDisabled && isMobileLogin && !isMobileVerificationDisabled {
|
||||||
|
log.Debug("Mobile basic authentication is disabled.")
|
||||||
|
return nil, fmt.Errorf(`mobile basic authentication is disabled for this instance`)
|
||||||
|
}
|
||||||
|
var user *models.User
|
||||||
|
if isEmailLogin {
|
||||||
|
user, err = db.Provider.GetUserByEmail(ctx, email)
|
||||||
|
} else {
|
||||||
|
user, err = db.Provider.GetUserByPhoneNumber(ctx, phoneNumber)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get user: ", err)
|
||||||
|
return nil, fmt.Errorf(`bad user credentials`)
|
||||||
|
}
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
_, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to generate nonce: ", err)
|
log.Debug("Failed to generate nonce: ", err)
|
||||||
return res, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if user.RevokedTimestamp != nil {
|
||||||
redirectURI := ""
|
log.Debug("User access is revoked")
|
||||||
// give higher preference to params redirect uri
|
return nil, fmt.Errorf(`user access has been revoked`)
|
||||||
if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" {
|
}
|
||||||
redirectURI = refs.StringValue(params.RedirectURI)
|
if isEmailLogin {
|
||||||
} else {
|
redirectURI := ""
|
||||||
redirectURI, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
// give higher preference to params redirect uri
|
||||||
if err != nil {
|
if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" {
|
||||||
log.Debug("ResetPasswordURL not found using default app url: ", err)
|
redirectURI = refs.StringValue(params.RedirectURI)
|
||||||
redirectURI = hostname + "/app/reset-password"
|
} else {
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, redirectURI)
|
redirectURI, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("ResetPasswordURL not found using default app url: ", err)
|
||||||
|
redirectURI = hostname + "/app/reset-password"
|
||||||
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, redirectURI)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
verificationToken, err := token.CreateVerificationToken(email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to create verification token", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = db.Provider.AddVerificationRequest(ctx, &models.VerificationRequest{
|
||||||
|
Token: verificationToken,
|
||||||
|
Identifier: constants.VerificationTypeForgotPassword,
|
||||||
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
|
Email: email,
|
||||||
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURI,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to add verification request", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// execute it as go routine so that we can reduce the api latency
|
||||||
|
go mailService.SendEmail([]string{email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
|
||||||
|
"user": user.ToMap(),
|
||||||
|
"organization": utils.GetOrganization(),
|
||||||
|
"verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI),
|
||||||
|
})
|
||||||
|
return &model.ForgotPasswordResponse{
|
||||||
|
Message: `Please check your inbox! We have sent a password reset link.`,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
if isMobileLogin {
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI)
|
expiresAt := time.Now().Add(1 * time.Minute).Unix()
|
||||||
if err != nil {
|
otp := utils.GenerateOTP()
|
||||||
log.Debug("Failed to create verification token", err)
|
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
|
||||||
return res, err
|
Email: refs.StringValue(user.Email),
|
||||||
|
PhoneNumber: refs.StringValue(user.PhoneNumber),
|
||||||
|
Otp: otp,
|
||||||
|
ExpiresAt: expiresAt,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to add otp: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mfaSession := uuid.NewString()
|
||||||
|
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expiresAt)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to add mfasession: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cookie.SetMfaSession(gc, mfaSession)
|
||||||
|
smsBody := strings.Builder{}
|
||||||
|
smsBody.WriteString("Your verification code is: ")
|
||||||
|
smsBody.WriteString(otpData.Otp)
|
||||||
|
if err := smsproviders.SendSMS(phoneNumber, smsBody.String()); err != nil {
|
||||||
|
log.Debug("Failed to send sms: ", err)
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
return &model.ForgotPasswordResponse{
|
||||||
|
Message: "Please enter the OTP sent to your phone number and change your password.",
|
||||||
|
ShouldShowMobileOtpScreen: refs.NewBoolRef(true),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
_, err = db.Provider.AddVerificationRequest(ctx, &models.VerificationRequest{
|
return nil, fmt.Errorf(`email or phone number verification needs to be enabled`)
|
||||||
Token: verificationToken,
|
|
||||||
Identifier: constants.VerificationTypeForgotPassword,
|
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
|
||||||
Email: params.Email,
|
|
||||||
Nonce: nonceHash,
|
|
||||||
RedirectURI: redirectURI,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("Failed to add verification request", err)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute it as go routine so that we can reduce the api latency
|
|
||||||
go email.SendEmail([]string{params.Email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
|
|
||||||
"user": user.ToMap(),
|
|
||||||
"organization": utils.GetOrganization(),
|
|
||||||
"verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI),
|
|
||||||
})
|
|
||||||
|
|
||||||
res = &model.Response{
|
|
||||||
Message: `Please check your inbox! We have sent a password reset link.`,
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
}
|
||||||
|
@@ -106,7 +106,7 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
|
|||||||
}
|
}
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
Email: email,
|
Email: refs.NewStringRef(email),
|
||||||
Roles: strings.Join(defaultRoles, ","),
|
Roles: strings.Join(defaultRoles, ","),
|
||||||
}
|
}
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
@@ -171,7 +171,7 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 emailservice.SendEmail([]string{user.Email}, constants.VerificationTypeInviteMember, map[string]interface{}{
|
go emailservice.SendEmail([]string{refs.StringValue(user.Email)}, constants.VerificationTypeInviteMember, map[string]interface{}{
|
||||||
"user": user.ToMap(),
|
"user": user.ToMap(),
|
||||||
"organization": utils.GetOrganization(),
|
"organization": utils.GetOrganization(),
|
||||||
"verification_url": utils.GetInviteVerificationURL(verifyEmailURL, verificationToken, redirectURL),
|
"verification_url": utils.GetInviteVerificationURL(verifyEmailURL, verificationToken, redirectURL),
|
||||||
|
44
server/resolvers/is_registered.go
Normal file
44
server/resolvers/is_registered.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package resolvers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
"github.com/authorizerdev/authorizer/server/refs"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsRegisteredResolver is a resolver for registered checkup query
|
||||||
|
func IsRegisteredResolver(ctx context.Context, email string) (*model.AuthResponse, error) {
|
||||||
|
var res *model.AuthResponse
|
||||||
|
email = strings.TrimSpace(refs.StringValue(&email))
|
||||||
|
if email == "" {
|
||||||
|
log.Debug("Email is required")
|
||||||
|
return res, fmt.Errorf(`email is required`)
|
||||||
|
}
|
||||||
|
|
||||||
|
log := log.WithField("email", email)
|
||||||
|
|
||||||
|
// find user with email
|
||||||
|
existingUser, err := db.Provider.GetUserByEmail(ctx, email)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get user by email: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingUser != nil {
|
||||||
|
res.Message = "registered"
|
||||||
|
if existingUser.EmailVerifiedAt != nil {
|
||||||
|
res.Message = "verified"
|
||||||
|
log.Debug("Email is already verified and signed up.")
|
||||||
|
return res, fmt.Errorf(`%s has already signed up`, email)
|
||||||
|
} else if existingUser.ID != "" && existingUser.EmailVerifiedAt == nil {
|
||||||
|
res.Message = "not verified"
|
||||||
|
log.Debug("Email is already signed up. Verification pending...")
|
||||||
|
return res, fmt.Errorf("%s has already signed up. please complete the email verification process or reset the password", email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
@@ -7,23 +7,27 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/authenticators"
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
|
"github.com/authorizerdev/authorizer/server/crypto"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/email"
|
mailService "github.com/authorizerdev/authorizer/server/email"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
"github.com/authorizerdev/authorizer/server/refs"
|
"github.com/authorizerdev/authorizer/server/refs"
|
||||||
|
"github.com/authorizerdev/authorizer/server/smsproviders"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoginResolver is a resolver for login mutation
|
// LoginResolver is a resolver for login mutation
|
||||||
|
// User can login with email or phone number, but not both
|
||||||
func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error) {
|
func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error) {
|
||||||
var res *model.AuthResponse
|
var res *model.AuthResponse
|
||||||
|
|
||||||
@@ -33,49 +37,78 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
isBasiAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
|
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error getting basic auth disabled: ", err)
|
log.Debug("Error getting basic auth disabled: ", err)
|
||||||
isBasiAuthDisabled = true
|
isBasicAuthDisabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if isBasiAuthDisabled {
|
isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error getting mobile basic auth disabled: ", err)
|
||||||
|
isMobileBasicAuthDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
email := refs.StringValue(params.Email)
|
||||||
|
phoneNumber := refs.StringValue(params.PhoneNumber)
|
||||||
|
if email == "" && phoneNumber == "" {
|
||||||
|
log.Debug("Email or phone number is required")
|
||||||
|
return res, fmt.Errorf(`email or phone number is required`)
|
||||||
|
}
|
||||||
|
log := log.WithFields(log.Fields{
|
||||||
|
"email": refs.StringValue(params.Email),
|
||||||
|
"phone_number": refs.StringValue(params.PhoneNumber),
|
||||||
|
})
|
||||||
|
isEmailLogin := email != ""
|
||||||
|
isMobileLogin := phoneNumber != ""
|
||||||
|
if isBasicAuthDisabled {
|
||||||
log.Debug("Basic authentication is disabled.")
|
log.Debug("Basic authentication is disabled.")
|
||||||
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
||||||
}
|
}
|
||||||
|
if isMobileBasicAuthDisabled && isMobileLogin {
|
||||||
log := log.WithFields(log.Fields{
|
log.Debug("Mobile basic authentication is disabled.")
|
||||||
"email": params.Email,
|
return res, fmt.Errorf(`mobile basic authentication is disabled for this instance`)
|
||||||
})
|
}
|
||||||
params.Email = strings.ToLower(params.Email)
|
var user *models.User
|
||||||
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
|
if isEmailLogin {
|
||||||
if err != nil {
|
user, err = db.Provider.GetUserByEmail(ctx, email)
|
||||||
log.Debug("Failed to get user by email: ", err)
|
} else {
|
||||||
return res, fmt.Errorf(`bad user credentials`)
|
user, err = db.Provider.GetUserByPhoneNumber(ctx, phoneNumber)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get user: ", err)
|
||||||
|
return res, fmt.Errorf(`user not found`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.RevokedTimestamp != nil {
|
if user.RevokedTimestamp != nil {
|
||||||
log.Debug("User access is revoked")
|
log.Debug("User access is revoked")
|
||||||
return res, fmt.Errorf(`user access has been revoked`)
|
return res, fmt.Errorf(`user access has been revoked`)
|
||||||
}
|
}
|
||||||
|
if isEmailLogin {
|
||||||
|
if !strings.Contains(user.SignupMethods, constants.AuthRecipeMethodBasicAuth) {
|
||||||
|
log.Debug("User signup method is not basic auth")
|
||||||
|
return res, fmt.Errorf(`user has not signed up email & password`)
|
||||||
|
}
|
||||||
|
|
||||||
if !strings.Contains(user.SignupMethods, constants.AuthRecipeMethodBasicAuth) {
|
if user.EmailVerifiedAt == nil {
|
||||||
log.Debug("User signup method is not basic auth")
|
log.Debug("User email is not verified")
|
||||||
return res, fmt.Errorf(`user has not signed up email & password`)
|
return res, fmt.Errorf(`email not verified`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !strings.Contains(user.SignupMethods, constants.AuthRecipeMethodMobileBasicAuth) {
|
||||||
|
log.Debug("User signup method is not mobile basic auth")
|
||||||
|
return res, fmt.Errorf(`user has not signed up with phone number & password`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.PhoneNumberVerifiedAt == nil {
|
||||||
|
log.Debug("User phone number is not verified")
|
||||||
|
return res, fmt.Errorf(`phone number is not verified`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
err = crypto.VerifyPassword(*user.Password, params.Password)
|
||||||
if user.EmailVerifiedAt == nil {
|
|
||||||
log.Debug("User email is not verified")
|
|
||||||
return res, fmt.Errorf(`email not verified`)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(params.Password))
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to compare password: ", err)
|
log.Debug("Failed to compare password: ", err)
|
||||||
return res, fmt.Errorf(`bad user credentials`)
|
return res, fmt.Errorf(`bad user credentials`)
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
defaultRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||||
roles := []string{}
|
roles := []string{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -84,62 +117,163 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
} else {
|
} else {
|
||||||
roles = strings.Split(defaultRolesString, ",")
|
roles = strings.Split(defaultRolesString, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
currentRoles := strings.Split(user.Roles, ",")
|
currentRoles := strings.Split(user.Roles, ",")
|
||||||
if len(params.Roles) > 0 {
|
if len(params.Roles) > 0 {
|
||||||
if !validators.IsValidRoles(params.Roles, currentRoles) {
|
if !validators.IsValidRoles(params.Roles, currentRoles) {
|
||||||
log.Debug("Invalid roles: ", params.Roles)
|
log.Debug("Invalid roles: ", params.Roles)
|
||||||
return res, fmt.Errorf(`invalid roles`)
|
return res, fmt.Errorf(`invalid roles`)
|
||||||
}
|
}
|
||||||
|
|
||||||
roles = params.Roles
|
roles = params.Roles
|
||||||
}
|
}
|
||||||
|
|
||||||
scope := []string{"openid", "email", "profile"}
|
scope := []string{"openid", "email", "profile"}
|
||||||
if params.Scope != nil && len(scope) > 0 {
|
if params.Scope != nil && len(scope) > 0 {
|
||||||
scope = params.Scope
|
scope = params.Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmailServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
|
isEmailServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
|
||||||
if err != nil || !isEmailServiceEnabled {
|
if err != nil || !isEmailServiceEnabled {
|
||||||
log.Debug("Email service not enabled: ", err)
|
log.Debug("Email service not enabled: ", err)
|
||||||
}
|
}
|
||||||
|
isSMSServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsSMSServiceEnabled)
|
||||||
|
if err != nil || !isSMSServiceEnabled {
|
||||||
|
log.Debug("SMS service not enabled: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
isMFADisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication)
|
isMFADisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication)
|
||||||
if err != nil || !isMFADisabled {
|
if err != nil || !isMFADisabled {
|
||||||
log.Debug("MFA service not enabled: ", err)
|
log.Debug("MFA service not enabled: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If email service is not enabled continue the process in any way
|
isTOTPLoginDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableTOTPLogin)
|
||||||
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && isEmailServiceEnabled && !isMFADisabled {
|
if err != nil || !isTOTPLoginDisabled {
|
||||||
|
log.Debug("totp service not enabled: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
isMailOTPDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMailOTPLogin)
|
||||||
|
if err != nil || !isMailOTPDisabled {
|
||||||
|
log.Debug("mail OTP service not enabled: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
isSMSOTPDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
|
||||||
|
if err != nil || !isSMSOTPDisabled {
|
||||||
|
log.Debug("sms OTP service not enabled: ", err)
|
||||||
|
}
|
||||||
|
setOTPMFaSession := func(expiresAt int64) error {
|
||||||
|
mfaSession := uuid.NewString()
|
||||||
|
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expiresAt)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to add mfasession: ", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cookie.SetMfaSession(gc, mfaSession)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// If multi factor authentication is enabled and we need to generate OTP for mail / sms based MFA
|
||||||
|
generateOTP := func(expiresAt int64) (*models.OTP, error) {
|
||||||
otp := utils.GenerateOTP()
|
otp := utils.GenerateOTP()
|
||||||
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
|
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
|
||||||
Email: user.Email,
|
Email: refs.StringValue(user.Email),
|
||||||
Otp: otp,
|
PhoneNumber: refs.StringValue(user.PhoneNumber),
|
||||||
ExpiresAt: time.Now().Add(1 * time.Minute).Unix(),
|
Otp: otp,
|
||||||
|
ExpiresAt: expiresAt,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to add otp: ", err)
|
log.Debug("Failed to add otp: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return otpData, nil
|
||||||
|
}
|
||||||
|
// If multi factor authentication is enabled and is email based login and email otp is enabled
|
||||||
|
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMFADisabled && !isMailOTPDisabled && isEmailServiceEnabled && isEmailLogin {
|
||||||
|
expiresAt := time.Now().Add(1 * time.Minute).Unix()
|
||||||
|
otpData, err := generateOTP(expiresAt)
|
||||||
go func() {
|
go func() {
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to generate otp: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := setOTPMFaSession(expiresAt); err != nil {
|
||||||
|
log.Debug("Failed to set mfa session: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
// exec it as go routine so that we can reduce the api latency
|
// exec it as go routine so that we can reduce the api latency
|
||||||
go email.SendEmail([]string{params.Email}, constants.VerificationTypeOTP, map[string]interface{}{
|
if err := mailService.SendEmail([]string{email}, constants.VerificationTypeOTP, map[string]interface{}{
|
||||||
"user": user.ToMap(),
|
"user": user.ToMap(),
|
||||||
"organization": utils.GetOrganization(),
|
"organization": utils.GetOrganization(),
|
||||||
"otp": otpData.Otp,
|
"otp": otpData.Otp,
|
||||||
})
|
}); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Debug("Failed to send otp email: ", err)
|
log.Debug("Failed to send otp email: ", err)
|
||||||
}
|
}
|
||||||
|
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return &model.AuthResponse{
|
return &model.AuthResponse{
|
||||||
Message: "Please check the OTP in your inbox",
|
Message: "Please check email inbox for the OTP",
|
||||||
ShouldShowEmailOtpScreen: refs.NewBoolRef(true),
|
ShouldShowEmailOtpScreen: refs.NewBoolRef(isMobileLogin),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
// If multi factor authentication is enabled and is sms based login and sms otp is enabled
|
||||||
|
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMFADisabled && !isSMSOTPDisabled && isSMSServiceEnabled && isMobileLogin {
|
||||||
|
expiresAt := time.Now().Add(1 * time.Minute).Unix()
|
||||||
|
otpData, err := generateOTP(expiresAt)
|
||||||
|
go func() {
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to generate otp: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := setOTPMFaSession(expiresAt); err != nil {
|
||||||
|
log.Debug("Failed to set mfa session: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
smsBody := strings.Builder{}
|
||||||
|
smsBody.WriteString("Your verification code is: ")
|
||||||
|
smsBody.WriteString(otpData.Otp)
|
||||||
|
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
|
||||||
|
if err := smsproviders.SendSMS(phoneNumber, smsBody.String()); err != nil {
|
||||||
|
log.Debug("Failed to send sms: ", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return &model.AuthResponse{
|
||||||
|
Message: "Please check text message for the OTP",
|
||||||
|
ShouldShowMobileOtpScreen: refs.NewBoolRef(isMobileLogin),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
// If mfa enabled and also totp enabled
|
||||||
|
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && !isMFADisabled && !isTOTPLoginDisabled {
|
||||||
|
expiresAt := time.Now().Add(3 * time.Minute).Unix()
|
||||||
|
if err := setOTPMFaSession(expiresAt); err != nil {
|
||||||
|
log.Debug("Failed to set mfa session: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
authenticator, err := db.Provider.GetAuthenticatorDetailsByUserId(ctx, user.ID, constants.EnvKeyTOTPAuthenticator)
|
||||||
|
if err != nil || authenticator == nil || authenticator.VerifiedAt == nil {
|
||||||
|
// generate totp
|
||||||
|
// Generate a base64 URL and initiate the registration for TOTP
|
||||||
|
authConfig, err := authenticators.Provider.Generate(ctx, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("error while generating base64 url: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
recoveryCodes := []*string{}
|
||||||
|
for _, code := range authConfig.RecoveryCodes {
|
||||||
|
recoveryCodes = append(recoveryCodes, refs.NewStringRef(code))
|
||||||
|
}
|
||||||
|
// when user is first time registering for totp
|
||||||
|
res = &model.AuthResponse{
|
||||||
|
Message: `Proceed to totp verification screen`,
|
||||||
|
ShouldShowTotpScreen: refs.NewBoolRef(true),
|
||||||
|
AuthenticatorScannerImage: refs.NewStringRef(authConfig.ScannerImage),
|
||||||
|
AuthenticatorSecret: refs.NewStringRef(authConfig.Secret),
|
||||||
|
AuthenticatorRecoveryCodes: recoveryCodes,
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
} else {
|
||||||
|
//when user is already register for totp
|
||||||
|
res = &model.AuthResponse{
|
||||||
|
Message: `Proceed to totp screen`,
|
||||||
|
ShouldShowTotpScreen: refs.NewBoolRef(true),
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
code := ""
|
code := ""
|
||||||
codeChallenge := ""
|
codeChallenge := ""
|
||||||
@@ -162,7 +296,6 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
if nonce == "" {
|
if nonce == "" {
|
||||||
nonce = uuid.New().String()
|
nonce = uuid.New().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, code)
|
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to create auth token", err)
|
log.Debug("Failed to create auth token", err)
|
||||||
@@ -202,7 +335,13 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
|
// Register event
|
||||||
|
if isEmailLogin {
|
||||||
|
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
|
||||||
|
} else {
|
||||||
|
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
|
||||||
|
}
|
||||||
|
// Record session
|
||||||
db.Provider.AddSession(ctx, &models.Session{
|
db.Provider.AddSession(ctx, &models.Session{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
UserAgent: utils.GetUserAgent(gc.Request),
|
UserAgent: utils.GetUserAgent(gc.Request),
|
||||||
|
@@ -2,12 +2,10 @@ package resolvers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"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/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
@@ -22,31 +20,18 @@ func LogoutResolver(ctx context.Context) (*model.Response, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get fingerprint hash
|
tokenData, err := token.GetUserIDFromSessionOrAccessToken(gc)
|
||||||
fingerprintHash, err := cookie.GetSession(gc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to get fingerprint hash: ", err)
|
log.Debug("Failed GetUserIDFromSessionOrAccessToken: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptedFingerPrint, err := crypto.DecryptAES(fingerprintHash)
|
sessionKey := tokenData.UserID
|
||||||
if err != nil {
|
if tokenData.LoginMethod != "" {
|
||||||
log.Debug("Failed to decrypt fingerprint hash: ", err)
|
sessionKey = tokenData.LoginMethod + ":" + tokenData.UserID
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sessionData token.SessionData
|
memorystore.Provider.DeleteUserSession(sessionKey, tokenData.Nonce)
|
||||||
err = json.Unmarshal([]byte(decryptedFingerPrint), &sessionData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionKey := sessionData.Subject
|
|
||||||
if sessionData.LoginMethod != "" {
|
|
||||||
sessionKey = sessionData.LoginMethod + ":" + sessionData.Subject
|
|
||||||
}
|
|
||||||
|
|
||||||
memorystore.Provider.DeleteUserSession(sessionKey, sessionData.Nonce)
|
|
||||||
cookie.DeleteSession(gc)
|
cookie.DeleteSession(gc)
|
||||||
|
|
||||||
res := &model.Response{
|
res := &model.Response{
|
||||||
|
@@ -56,7 +56,7 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
|||||||
inputRoles := []string{}
|
inputRoles := []string{}
|
||||||
|
|
||||||
user := &models.User{
|
user := &models.User{
|
||||||
Email: params.Email,
|
Email: refs.NewStringRef(params.Email),
|
||||||
}
|
}
|
||||||
|
|
||||||
// find user with email
|
// find user with email
|
||||||
|
@@ -106,6 +106,16 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
|
|||||||
log.Debug("Failed to get Disable Basic Authentication from environment variable", err)
|
log.Debug("Failed to get Disable Basic Authentication from environment variable", err)
|
||||||
isBasicAuthDisabled = true
|
isBasicAuthDisabled = true
|
||||||
}
|
}
|
||||||
|
isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get Disable Basic Authentication from environment variable", err)
|
||||||
|
isMobileBasicAuthDisabled = true
|
||||||
|
}
|
||||||
|
isMobileVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get Disable Basic Authentication from environment variable", err)
|
||||||
|
isMobileVerificationDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
|
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -138,21 +148,23 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
metaInfo := model.Meta{
|
metaInfo := model.Meta{
|
||||||
Version: constants.VERSION,
|
Version: constants.VERSION,
|
||||||
ClientID: clientID,
|
ClientID: clientID,
|
||||||
IsGoogleLoginEnabled: googleClientID != "" && googleClientSecret != "",
|
IsGoogleLoginEnabled: googleClientID != "" && googleClientSecret != "",
|
||||||
IsGithubLoginEnabled: githubClientID != "" && githubClientSecret != "",
|
IsGithubLoginEnabled: githubClientID != "" && githubClientSecret != "",
|
||||||
IsFacebookLoginEnabled: facebookClientID != "" && facebookClientSecret != "",
|
IsFacebookLoginEnabled: facebookClientID != "" && facebookClientSecret != "",
|
||||||
IsLinkedinLoginEnabled: linkedClientID != "" && linkedInClientSecret != "",
|
IsLinkedinLoginEnabled: linkedClientID != "" && linkedInClientSecret != "",
|
||||||
IsAppleLoginEnabled: appleClientID != "" && appleClientSecret != "",
|
IsAppleLoginEnabled: appleClientID != "" && appleClientSecret != "",
|
||||||
IsTwitterLoginEnabled: twitterClientID != "" && twitterClientSecret != "",
|
IsTwitterLoginEnabled: twitterClientID != "" && twitterClientSecret != "",
|
||||||
IsMicrosoftLoginEnabled: microsoftClientID != "" && microsoftClientSecret != "",
|
IsMicrosoftLoginEnabled: microsoftClientID != "" && microsoftClientSecret != "",
|
||||||
IsBasicAuthenticationEnabled: !isBasicAuthDisabled,
|
IsBasicAuthenticationEnabled: !isBasicAuthDisabled,
|
||||||
IsEmailVerificationEnabled: !isEmailVerificationDisabled,
|
IsEmailVerificationEnabled: !isEmailVerificationDisabled,
|
||||||
IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled,
|
IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled,
|
||||||
IsSignUpEnabled: !isSignUpDisabled,
|
IsSignUpEnabled: !isSignUpDisabled,
|
||||||
IsStrongPasswordEnabled: !isStrongPasswordDisabled,
|
IsStrongPasswordEnabled: !isStrongPasswordDisabled,
|
||||||
IsMultiFactorAuthEnabled: !isMultiFactorAuthenticationEnabled,
|
IsMultiFactorAuthEnabled: !isMultiFactorAuthenticationEnabled,
|
||||||
|
IsMobileBasicAuthenticationEnabled: !isMobileBasicAuthDisabled,
|
||||||
|
IsPhoneVerificationEnabled: !isMobileVerificationDisabled,
|
||||||
}
|
}
|
||||||
return &metaInfo, nil
|
return &metaInfo, nil
|
||||||
}
|
}
|
||||||
|
@@ -8,10 +8,10 @@ import (
|
|||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
|
"github.com/authorizerdev/authorizer/server/crypto"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
@@ -33,13 +33,13 @@ func MobileLoginResolver(ctx context.Context, params model.MobileLoginInput) (*m
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
isBasiAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
|
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error getting mobile basic auth disabled: ", err)
|
log.Debug("Error getting mobile basic auth disabled: ", err)
|
||||||
isBasiAuthDisabled = true
|
isBasicAuthDisabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if isBasiAuthDisabled {
|
if isBasicAuthDisabled {
|
||||||
log.Debug("Basic authentication is disabled.")
|
log.Debug("Basic authentication is disabled.")
|
||||||
return res, fmt.Errorf(`phone number based basic authentication is disabled for this instance`)
|
return res, fmt.Errorf(`phone number based basic authentication is disabled for this instance`)
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ func MobileLoginResolver(ctx context.Context, params model.MobileLoginInput) (*m
|
|||||||
return res, fmt.Errorf(`phone number is not verified`)
|
return res, fmt.Errorf(`phone number is not verified`)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(params.Password))
|
err = crypto.VerifyPassword(*user.Password, params.Password)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to compare password: ", err)
|
log.Debug("Failed to compare password: ", err)
|
||||||
@@ -122,15 +122,25 @@ func MobileLoginResolver(ctx context.Context, params model.MobileLoginInput) (*m
|
|||||||
smsBody := strings.Builder{}
|
smsBody := strings.Builder{}
|
||||||
smsBody.WriteString("Your verification code is: ")
|
smsBody.WriteString("Your verification code is: ")
|
||||||
smsBody.WriteString(smsCode)
|
smsBody.WriteString(smsCode)
|
||||||
|
expires := time.Now().Add(duration).Unix()
|
||||||
_, err := db.Provider.UpsertOTP(ctx, &models.OTP{
|
_, err := db.Provider.UpsertOTP(ctx, &models.OTP{
|
||||||
PhoneNumber: params.PhoneNumber,
|
PhoneNumber: params.PhoneNumber,
|
||||||
Otp: smsCode,
|
Otp: smsCode,
|
||||||
ExpiresAt: time.Now().Add(duration).Unix(),
|
ExpiresAt: expires,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("error while upserting OTP: ", err.Error())
|
log.Debug("error while upserting OTP: ", err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mfaSession := uuid.NewString()
|
||||||
|
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expires)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to add mfasession: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cookie.SetMfaSession(gc, mfaSession)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
|
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
|
||||||
smsproviders.SendSMS(params.PhoneNumber, smsBody.String())
|
smsproviders.SendSMS(params.PhoneNumber, smsBody.String())
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user