Merge branch 'main' of https://github.com/manojown/authorizer into feat/couchbase-db-support
This commit is contained in:
commit
2052b850a3
43
.github/workflows/release.yaml
vendored
43
.github/workflows/release.yaml
vendored
|
@ -19,19 +19,27 @@ on:
|
|||
|
||||
jobs:
|
||||
releases:
|
||||
name: Release Authorizer Binary
|
||||
name: Release Authorizer
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- # Add support for more platforms with QEMU (optional)
|
||||
# https://github.com/docker/setup-qemu-action
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.17.3'
|
||||
go-version: '^1.19.1'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install build-essential wget zip gcc-mingw-w64 && \
|
||||
sudo apt-get install build-essential wget zip libc6-dev-arm64-cross && \
|
||||
echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH && \
|
||||
wget --no-check-certificate --progress=dot:mega https://github.com/wangyoucao577/assets-uploader/releases/download/v0.3.0/github-assets-uploader-v0.3.0-linux-amd64.tar.gz -O github-assets-uploader.tar.gz && \
|
||||
tar -zxf github-assets-uploader.tar.gz && \
|
||||
|
@ -44,25 +52,26 @@ jobs:
|
|||
run: whereis go
|
||||
- name: Print Go Version
|
||||
run: go version
|
||||
- name: Install gox
|
||||
run: go install github.com/mitchellh/gox@latest
|
||||
- name: Set VERSION env
|
||||
run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV}
|
||||
- name: Copy .env file
|
||||
run: mv .env.sample .env
|
||||
- name: Package files for windows
|
||||
- name: Build package
|
||||
run: |
|
||||
make clean && \
|
||||
CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \
|
||||
mv build/server build/server.exe && \
|
||||
zip -vr authorizer-${VERSION}-windows-amd64.zip .env app/build build templates dashboard/build
|
||||
- name: Package files for linux
|
||||
run: |
|
||||
make clean && \
|
||||
CGO_ENABLED=1 make && \
|
||||
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app/build build templates dashboard/build
|
||||
make build && \
|
||||
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-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
|
||||
- name: Upload assets
|
||||
run: |
|
||||
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} && \
|
||||
github-assets-uploader -f authorizer-${VERSION}-darwin-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||
github-assets-uploader -f authorizer-${VERSION}-linux-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
|
@ -74,6 +83,11 @@ jobs:
|
|||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: lakhansamani/authorizer
|
||||
tags: |
|
||||
type=schedule
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
type=ref,event=pr
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
|
@ -82,5 +96,6 @@ jobs:
|
|||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,3 +16,5 @@ test.db
|
|||
.yalc
|
||||
yalc.lock
|
||||
certs/
|
||||
*-shm
|
||||
*-wal
|
10
Makefile
10
Makefile
|
@ -3,6 +3,12 @@ VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
|
|||
|
||||
cmd:
|
||||
cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server'
|
||||
build:
|
||||
cd server && gox \
|
||||
-osarch="linux/amd64 linux/arm64 darwin/amd64 windows/amd64" \
|
||||
-ldflags "-w -X main.VERSION=$(VERSION)" \
|
||||
-output="../build/{{.OS}}/{{.Arch}}/server" \
|
||||
./...
|
||||
build-app:
|
||||
cd app && npm i && npm run build
|
||||
build-dashboard:
|
||||
|
@ -10,7 +16,7 @@ build-dashboard:
|
|||
clean:
|
||||
rm -rf build
|
||||
test:
|
||||
rm -rf server/test/test.db && rm -rf test.db && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test
|
||||
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test
|
||||
test-mongodb:
|
||||
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
||||
cd server && go clean --testcache && TEST_DBS="mongodb" go test -p 1 -v ./test
|
||||
|
@ -32,7 +38,7 @@ test-couchbase:
|
|||
cd server && go clean --testcache && TEST_DBS="couchbase" go test -p 1 -v ./test
|
||||
# docker rm -vf couchbase-local-test
|
||||
test-all-db:
|
||||
rm -rf server/test/test.db && rm -rf test.db
|
||||
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal
|
||||
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
|
||||
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
|
||||
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.8.4
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false
|
||||
"useTabs": true
|
||||
}
|
||||
|
|
101
app/package-lock.json
generated
101
app/package-lock.json
generated
|
@ -9,7 +9,7 @@
|
|||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^1.1.2",
|
||||
"@authorizerdev/authorizer-react": "^1.1.4",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
|
@ -27,9 +27,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@authorizerdev/authorizer-js": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.0.tgz",
|
||||
"integrity": "sha512-MdEw1SjhIm7pXq20AscHSbnAta2PC3w7GNBY52/OzmlBXUGH3ooUQX/aszbYOse3FlhapcrGrRvg4sNM7faGAg==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.2.tgz",
|
||||
"integrity": "sha512-22qoqBaCNMn3QRWdJXmwAZeb5X9lwhZF3y23loY0eO3xUUzBaJiltENjHynbLGCg8LGgn7UaJEKDqGfL6Rzwvg==",
|
||||
"dependencies": {
|
||||
"cross-fetch": "^3.1.5"
|
||||
},
|
||||
|
@ -38,14 +38,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@authorizerdev/authorizer-react": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.2.tgz",
|
||||
"integrity": "sha512-uBmuKnOVX8gp8CEUuGJuz04ep+8qMEzJXWd5leEGKYMIgolHpu/lOinnMUXhjh8YL3pA4+EhvB+hQXxUX+rRHQ==",
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.4.tgz",
|
||||
"integrity": "sha512-FBH2igXFM8+TdA2hl1S/HMzt1+OL5wWUow3+Zyiq+IkG9nIjWFlM7ebo4D0zJd875IJiabYFnXqstRABo0ysIQ==",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-js": "^1.1.0",
|
||||
"final-form": "^4.20.2",
|
||||
"react-final-form": "^6.5.3",
|
||||
"styled-components": "^5.3.0"
|
||||
"@authorizerdev/authorizer-js": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
@ -469,18 +466,6 @@
|
|||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/final-form": {
|
||||
"version": "4.20.4",
|
||||
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.4.tgz",
|
||||
"integrity": "sha512-hyoOVVilPLpkTvgi+FSJkFZrh0Yhy4BhE6lk/NiBwrF4aRV8/ykKEyXYvQH/pfUbRkOosvpESYouFb+FscsLrw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/final-form"
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
|
@ -673,33 +658,6 @@
|
|||
"react": "17.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-final-form": {
|
||||
"version": "6.5.7",
|
||||
"resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.7.tgz",
|
||||
"integrity": "sha512-o7tvJXB+McGiXOILqIC8lnOcX4aLhIBiF/Xi9Qet35b7XOS8R7KL8HLRKTfnZWQJm6MCE15v1U0SFive0NcxyA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/final-form"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"final-form": "4.20.4",
|
||||
"react": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-final-form/node_modules/@babel/runtime": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz",
|
||||
"integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
|
@ -876,22 +834,19 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-js": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.0.tgz",
|
||||
"integrity": "sha512-MdEw1SjhIm7pXq20AscHSbnAta2PC3w7GNBY52/OzmlBXUGH3ooUQX/aszbYOse3FlhapcrGrRvg4sNM7faGAg==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.1.2.tgz",
|
||||
"integrity": "sha512-22qoqBaCNMn3QRWdJXmwAZeb5X9lwhZF3y23loY0eO3xUUzBaJiltENjHynbLGCg8LGgn7UaJEKDqGfL6Rzwvg==",
|
||||
"requires": {
|
||||
"cross-fetch": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"@authorizerdev/authorizer-react": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.2.tgz",
|
||||
"integrity": "sha512-uBmuKnOVX8gp8CEUuGJuz04ep+8qMEzJXWd5leEGKYMIgolHpu/lOinnMUXhjh8YL3pA4+EhvB+hQXxUX+rRHQ==",
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.4.tgz",
|
||||
"integrity": "sha512-FBH2igXFM8+TdA2hl1S/HMzt1+OL5wWUow3+Zyiq+IkG9nIjWFlM7ebo4D0zJd875IJiabYFnXqstRABo0ysIQ==",
|
||||
"requires": {
|
||||
"@authorizerdev/authorizer-js": "^1.1.0",
|
||||
"final-form": "^4.20.2",
|
||||
"react-final-form": "^6.5.3",
|
||||
"styled-components": "^5.3.0"
|
||||
"@authorizerdev/authorizer-js": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
|
@ -1231,14 +1186,6 @@
|
|||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"final-form": {
|
||||
"version": "4.20.4",
|
||||
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.4.tgz",
|
||||
"integrity": "sha512-hyoOVVilPLpkTvgi+FSJkFZrh0Yhy4BhE6lk/NiBwrF4aRV8/ykKEyXYvQH/pfUbRkOosvpESYouFb+FscsLrw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.10.0"
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
|
@ -1387,24 +1334,6 @@
|
|||
"scheduler": "^0.20.2"
|
||||
}
|
||||
},
|
||||
"react-final-form": {
|
||||
"version": "6.5.7",
|
||||
"resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.7.tgz",
|
||||
"integrity": "sha512-o7tvJXB+McGiXOILqIC8lnOcX4aLhIBiF/Xi9Qet35b7XOS8R7KL8HLRKTfnZWQJm6MCE15v1U0SFive0NcxyA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.15.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz",
|
||||
"integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
"scripts": {
|
||||
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
|
||||
"start": "NODE_ENV=development node ./esbuild.config.js",
|
||||
"format": "prettier --write --use-tabs 'src/**/*.(ts|tsx|js|jsx)'"
|
||||
"format": "prettier --write 'src/**/*.(ts|tsx|js|jsx)'"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Lakhan Samani",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@authorizerdev/authorizer-react": "^1.1.2",
|
||||
"@authorizerdev/authorizer-react": "^1.1.4",
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"esbuild": "^0.12.17",
|
||||
|
|
|
@ -38,6 +38,8 @@ export default function Root({
|
|||
const scope = searchParams.get('scope')
|
||||
? searchParams.get('scope')?.toString().split(' ')
|
||||
: ['openid', 'profile', 'email'];
|
||||
const code = searchParams.get('code') || '';
|
||||
const nonce = searchParams.get('nonce') || '';
|
||||
|
||||
const urlProps: Record<string, any> = {
|
||||
state,
|
||||
|
@ -58,9 +60,19 @@ export default function Root({
|
|||
if (token) {
|
||||
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}`;
|
||||
|
||||
if (code !== '') {
|
||||
params += `&code=${code}`;
|
||||
}
|
||||
|
||||
if (nonce !== '') {
|
||||
params += `&nonce=${nonce}`;
|
||||
}
|
||||
|
||||
if (token.refresh_token) {
|
||||
params += `&refresh_token=${token.refresh_token}`;
|
||||
}
|
||||
|
||||
const url = new URL(redirectURL);
|
||||
if (redirectURL.includes('?')) {
|
||||
redirectURL = `${redirectURL}&${params}`;
|
||||
|
@ -74,7 +86,7 @@ export default function Root({
|
|||
}
|
||||
}
|
||||
return () => {};
|
||||
}, [token]);
|
||||
}, [token, config]);
|
||||
|
||||
if (loading) {
|
||||
return <h1>Loading...</h1>;
|
||||
|
@ -100,7 +112,7 @@ export default function Root({
|
|||
<Route path="/app" exact>
|
||||
<Login urlProps={urlProps} />
|
||||
</Route>
|
||||
<Route path="/app/signup" exact>
|
||||
<Route path="/app/signup">
|
||||
<SignUp urlProps={urlProps} />
|
||||
</Route>
|
||||
<Route path="/app/reset-password">
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false
|
||||
"useTabs": true
|
||||
}
|
||||
|
|
1079
dashboard/package-lock.json
generated
1079
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -29,6 +29,10 @@ import {
|
|||
Tbody,
|
||||
Td,
|
||||
Code,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Stack,
|
||||
Textarea,
|
||||
} from '@chakra-ui/react';
|
||||
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
|
||||
import { useClient } from 'urql';
|
||||
|
@ -38,6 +42,7 @@ import {
|
|||
EmailTemplateInputDataFields,
|
||||
emailTemplateEventNames,
|
||||
emailTemplateVariables,
|
||||
EmailTemplateEditors,
|
||||
} from '../constants';
|
||||
import { capitalizeFirstLetter } from '../utils';
|
||||
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
|
||||
|
@ -66,6 +71,8 @@ interface templateVariableDataTypes {
|
|||
interface emailTemplateDataType {
|
||||
[EmailTemplateInputDataFields.EVENT_NAME]: string;
|
||||
[EmailTemplateInputDataFields.SUBJECT]: string;
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
||||
[EmailTemplateInputDataFields.DESIGN]: string;
|
||||
}
|
||||
|
||||
interface validatorDataType {
|
||||
|
@ -75,6 +82,8 @@ interface validatorDataType {
|
|||
const initTemplateData: emailTemplateDataType = {
|
||||
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
|
||||
[EmailTemplateInputDataFields.SUBJECT]: '',
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||
};
|
||||
|
||||
const initTemplateValidatorData: validatorDataType = {
|
||||
|
@ -91,6 +100,9 @@ const UpdateEmailTemplate = ({
|
|||
const emailEditorRef = useRef(null);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [editor, setEditor] = useState<string>(
|
||||
EmailTemplateEditors.PLAIN_HTML_EDITOR,
|
||||
);
|
||||
const [templateVariables, setTemplateVariables] = useState<
|
||||
templateVariableDataTypes[]
|
||||
>([]);
|
||||
|
@ -107,9 +119,11 @@ const UpdateEmailTemplate = ({
|
|||
if (selectedTemplate) {
|
||||
const { design } = selectedTemplate;
|
||||
try {
|
||||
if (design) {
|
||||
const designData = JSON.parse(design);
|
||||
// @ts-ignore
|
||||
emailEditorRef.current.editor.loadDesign(designData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
onClose();
|
||||
|
@ -136,24 +150,7 @@ const UpdateEmailTemplate = ({
|
|||
);
|
||||
};
|
||||
|
||||
const saveData = async () => {
|
||||
if (!validateData()) return;
|
||||
setLoading(true);
|
||||
// @ts-ignore
|
||||
return await emailEditorRef.current.editor.exportHtml(async (data) => {
|
||||
const { design, html } = data;
|
||||
if (!html || !design) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
[EmailTemplateInputDataFields.EVENT_NAME]:
|
||||
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
||||
[EmailTemplateInputDataFields.SUBJECT]:
|
||||
templateData[EmailTemplateInputDataFields.SUBJECT],
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
|
||||
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
|
||||
};
|
||||
const updateTemplate = async (params: emailTemplateDataType) => {
|
||||
let res: any = {};
|
||||
if (
|
||||
view === UpdateModalViews.Edit &&
|
||||
|
@ -197,9 +194,41 @@ const UpdateEmailTemplate = ({
|
|||
setValidator({ ...initTemplateValidatorData });
|
||||
fetchEmailTemplatesData();
|
||||
}
|
||||
view === UpdateModalViews.ADD && onClose();
|
||||
});
|
||||
};
|
||||
|
||||
const saveData = async () => {
|
||||
if (!validateData()) return;
|
||||
setLoading(true);
|
||||
let params: emailTemplateDataType = {
|
||||
[EmailTemplateInputDataFields.EVENT_NAME]:
|
||||
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
||||
[EmailTemplateInputDataFields.SUBJECT]:
|
||||
templateData[EmailTemplateInputDataFields.SUBJECT],
|
||||
[EmailTemplateInputDataFields.TEMPLATE]:
|
||||
templateData[EmailTemplateInputDataFields.TEMPLATE],
|
||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||
};
|
||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
||||
// @ts-ignore
|
||||
await emailEditorRef.current.editor.exportHtml(async (data) => {
|
||||
const { design, html } = data;
|
||||
if (!html || !design) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
params = {
|
||||
...params,
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
|
||||
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
|
||||
};
|
||||
await updateTemplate(params);
|
||||
});
|
||||
} else {
|
||||
await updateTemplate(params);
|
||||
}
|
||||
view === UpdateModalViews.ADD && onClose();
|
||||
};
|
||||
|
||||
const resetData = () => {
|
||||
if (selectedTemplate) {
|
||||
setTemplateData(selectedTemplate);
|
||||
|
@ -207,6 +236,8 @@ const UpdateEmailTemplate = ({
|
|||
setTemplateData({ ...initTemplateData });
|
||||
}
|
||||
};
|
||||
|
||||
// set template data if edit modal is open
|
||||
useEffect(() => {
|
||||
if (
|
||||
isOpen &&
|
||||
|
@ -214,10 +245,12 @@ const UpdateEmailTemplate = ({
|
|||
selectedTemplate &&
|
||||
Object.keys(selectedTemplate || {}).length
|
||||
) {
|
||||
const { id, created_at, template, design, ...rest } = selectedTemplate;
|
||||
const { id, created_at, ...rest } = selectedTemplate;
|
||||
setTemplateData(rest);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
// set template variables
|
||||
useEffect(() => {
|
||||
const updatedTemplateVariables = Object.entries(
|
||||
emailTemplateVariables,
|
||||
|
@ -244,6 +277,51 @@ const UpdateEmailTemplate = ({
|
|||
setTemplateVariables(updatedTemplateVariables);
|
||||
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
|
||||
|
||||
// change editor
|
||||
useEffect(() => {
|
||||
if (isOpen && selectedTemplate) {
|
||||
const { design } = selectedTemplate;
|
||||
if (design) {
|
||||
setEditor(EmailTemplateEditors.UNLAYER_EDITOR);
|
||||
} else {
|
||||
setEditor(EmailTemplateEditors.PLAIN_HTML_EDITOR);
|
||||
}
|
||||
}
|
||||
}, [isOpen, selectedTemplate]);
|
||||
|
||||
// reset fields when editor is changed
|
||||
useEffect(() => {
|
||||
if (selectedTemplate?.design) {
|
||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
||||
setTemplateData({
|
||||
...templateData,
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate.template,
|
||||
[EmailTemplateInputDataFields.DESIGN]: selectedTemplate.design,
|
||||
});
|
||||
} else {
|
||||
setTemplateData({
|
||||
...templateData,
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||
});
|
||||
}
|
||||
} else if (selectedTemplate?.template) {
|
||||
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
||||
setTemplateData({
|
||||
...templateData,
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||
});
|
||||
} else {
|
||||
setTemplateData({
|
||||
...templateData,
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate?.template,
|
||||
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{view === UpdateModalViews.ADD ? (
|
||||
|
@ -414,7 +492,22 @@ const UpdateEmailTemplate = ({
|
|||
alignItems="center"
|
||||
marginBottom="2%"
|
||||
>
|
||||
Template Body
|
||||
<Flex flex="1">Template Body</Flex>
|
||||
<Flex flex="3">
|
||||
<RadioGroup
|
||||
onChange={(value) => setEditor(value)}
|
||||
value={editor}
|
||||
>
|
||||
<Stack direction="row" spacing="50px">
|
||||
<Radio value={EmailTemplateEditors.PLAIN_HTML_EDITOR}>
|
||||
Plain HTML
|
||||
</Radio>
|
||||
<Radio value={EmailTemplateEditors.UNLAYER_EDITOR}>
|
||||
Unlayer Editor
|
||||
</Radio>
|
||||
</Stack>
|
||||
</RadioGroup>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex
|
||||
width="100%"
|
||||
|
@ -423,7 +516,22 @@ const UpdateEmailTemplate = ({
|
|||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
>
|
||||
{editor === EmailTemplateEditors.UNLAYER_EDITOR ? (
|
||||
<EmailEditor ref={emailEditorRef} onReady={onReady} />
|
||||
) : (
|
||||
<Textarea
|
||||
value={templateData.template}
|
||||
onChange={(e) => {
|
||||
setTemplateData({
|
||||
...templateData,
|
||||
[EmailTemplateInputDataFields.TEMPLATE]: e.target.value,
|
||||
});
|
||||
}}
|
||||
placeholder="Template HTML"
|
||||
border="0"
|
||||
height="500px"
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
|
|
|
@ -337,3 +337,8 @@ export const webhookPayloadExample: string = `{
|
|||
},
|
||||
"auth_recipe":"google"
|
||||
}`;
|
||||
|
||||
export enum EmailTemplateEditors {
|
||||
UNLAYER_EDITOR = 'unlayer_editor',
|
||||
PLAIN_HTML_EDITOR = 'plain_html_editor',
|
||||
}
|
||||
|
|
19
server/constants/oauth2.go
Normal file
19
server/constants/oauth2.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package constants
|
||||
|
||||
const (
|
||||
// - query: for Authorization Code grant. 302 Found triggers redirect.
|
||||
ResponseModeQuery = "query"
|
||||
// - fragment: for Implicit grant. 302 Found triggers redirect.
|
||||
ResponseModeFragment = "fragment"
|
||||
// - form_post: 200 OK with response parameters embedded in an HTML form as hidden parameters.
|
||||
ResponseModeFormPost = "form_post"
|
||||
// - web_message: For Silent Authentication. Uses HTML5 web messaging.
|
||||
ResponseModeWebMessage = "web_message"
|
||||
|
||||
// For the Authorization Code grant, use response_type=code to include the authorization code.
|
||||
ResponseTypeCode = "code"
|
||||
// For the Implicit grant, use response_type=token to include an access token.
|
||||
ResponseTypeToken = "token"
|
||||
// For the Implicit grant of id_token, use response_type=id_token to include an identifier token.
|
||||
ResponseTypeIDToken = "id_token"
|
||||
)
|
|
@ -25,7 +25,7 @@ type User struct {
|
|||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"`
|
||||
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"`
|
||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"`
|
||||
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
|
||||
PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
|
||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at" dynamo:"phone_number_verified_at"`
|
||||
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"`
|
||||
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`
|
||||
|
|
|
@ -26,7 +26,6 @@ func NewProvider() (*provider, error) {
|
|||
config := aws.Config{
|
||||
MaxRetries: aws.Int(3),
|
||||
CredentialsChainVerboseErrors: aws.Bool(true), // for full error logs
|
||||
|
||||
}
|
||||
|
||||
if awsRegion != "" {
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
package sql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/driver/sqlserver"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
|
@ -37,10 +35,10 @@ func NewProvider() (*provider, error) {
|
|||
var sqlDB *gorm.DB
|
||||
var err error
|
||||
customLogger := logger.New(
|
||||
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
|
||||
logrus.StandardLogger(),
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second, // Slow SQL threshold
|
||||
LogLevel: logger.Silent, // Log level
|
||||
LogLevel: logger.Error, // Log level
|
||||
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
|
||||
Colorful: false, // Disable color
|
||||
},
|
||||
|
@ -61,7 +59,7 @@ func NewProvider() (*provider, error) {
|
|||
case constants.DbTypePostgres, constants.DbTypeYugabyte, constants.DbTypeCockroachDB:
|
||||
sqlDB, err = gorm.Open(postgres.Open(dbURL), ormConfig)
|
||||
case constants.DbTypeSqlite:
|
||||
sqlDB, err = gorm.Open(sqlite.Open(dbURL), ormConfig)
|
||||
sqlDB, err = gorm.Open(sqlite.Open(dbURL+"?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)"), ormConfig)
|
||||
case constants.DbTypeMysql, constants.DbTypeMariaDB, constants.DbTypePlanetScaleDB:
|
||||
sqlDB, err = gorm.Open(mysql.Open(dbURL), ormConfig)
|
||||
case constants.DbTypeSqlserver:
|
||||
|
@ -72,35 +70,43 @@ func NewProvider() (*provider, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// For sqlserver, handle uniqueness of phone_number manually via extra db call
|
||||
// during create and update mutation.
|
||||
if sqlDB.Migrator().HasConstraint(&models.User{}, "authorizer_users_phone_number_key") {
|
||||
err = sqlDB.Migrator().DropConstraint(&models.User{}, "authorizer_users_phone_number_key")
|
||||
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{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// IMPACT: Request user to manually delete: UQ_phone_number constraint
|
||||
// unique constraint on phone number does not work with multiple null values for sqlserver
|
||||
// for more information check https://stackoverflow.com/a/767702
|
||||
if dbType == constants.DbTypeSqlserver {
|
||||
var indexInfos []indexInfo
|
||||
// remove index on phone number if present with different name
|
||||
res := sqlDB.Raw("SELECT i.name AS index_name, i.type_desc AS index_algorithm, CASE i.is_unique WHEN 1 THEN 'TRUE' ELSE 'FALSE' END AS is_unique, ac.Name AS column_name FROM sys.tables AS t INNER JOIN sys.indexes AS i ON t.object_id = i.object_id INNER JOIN sys.index_columns AS ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id INNER JOIN sys.all_columns AS ac ON ic.object_id = ac.object_id AND ic.column_id = ac.column_id WHERE t.name = 'authorizer_users' AND SCHEMA_NAME(t.schema_id) = 'dbo';").Scan(&indexInfos)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
// if dbType == constants.DbTypeSqlserver {
|
||||
// var indexInfos []indexInfo
|
||||
// // remove index on phone number if present with different name
|
||||
// res := sqlDB.Raw("SELECT i.name AS index_name, i.type_desc AS index_algorithm, CASE i.is_unique WHEN 1 THEN 'TRUE' ELSE 'FALSE' END AS is_unique, ac.Name AS column_name FROM sys.tables AS t INNER JOIN sys.indexes AS i ON t.object_id = i.object_id INNER JOIN sys.index_columns AS ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id INNER JOIN sys.all_columns AS ac ON ic.object_id = ac.object_id AND ic.column_id = ac.column_id WHERE t.name = 'authorizer_users' AND SCHEMA_NAME(t.schema_id) = 'dbo';").Scan(&indexInfos)
|
||||
// if res.Error != nil {
|
||||
// return nil, res.Error
|
||||
// }
|
||||
|
||||
for _, val := range indexInfos {
|
||||
if val.ColumnName == phoneNumberColumnName && val.IndexName != phoneNumberIndexName {
|
||||
// drop index & create new
|
||||
if res := sqlDB.Exec(fmt.Sprintf(`ALTER TABLE authorizer_users DROP CONSTRAINT "%s";`, val.IndexName)); res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
// for _, val := range indexInfos {
|
||||
// if val.ColumnName == phoneNumberColumnName && val.IndexName != phoneNumberIndexName {
|
||||
// // drop index & create new
|
||||
// if res := sqlDB.Exec(fmt.Sprintf(`ALTER TABLE authorizer_users DROP CONSTRAINT "%s";`, val.IndexName)); res.Error != nil {
|
||||
// return nil, res.Error
|
||||
// }
|
||||
|
||||
// create index
|
||||
if res := sqlDB.Exec(fmt.Sprintf("CREATE UNIQUE NONCLUSTERED INDEX %s ON authorizer_users(phone_number) WHERE phone_number IS NOT NULL;", phoneNumberIndexName)); res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// // create index
|
||||
// if res := sqlDB.Exec(fmt.Sprintf("CREATE UNIQUE NONCLUSTERED INDEX %s ON authorizer_users(phone_number) WHERE phone_number IS NOT NULL;", phoneNumberIndexName)); res.Error != nil {
|
||||
// return nil, res.Error
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return &provider{
|
||||
db: sqlDB,
|
||||
|
|
|
@ -2,12 +2,15 @@ package sql
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
|
@ -27,6 +30,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
|
|||
user.Roles = defaultRoles
|
||||
}
|
||||
|
||||
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
|
||||
if u, _ := p.GetUserByPhone(ctx, refs.StringValue(user.PhoneNumber)); u != nil {
|
||||
return user, fmt.Errorf("user with given phone number already exists")
|
||||
}
|
||||
}
|
||||
|
||||
user.CreatedAt = time.Now().Unix()
|
||||
user.UpdatedAt = time.Now().Unix()
|
||||
user.Key = user.ID
|
||||
|
@ -47,6 +56,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
|
|||
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
|
||||
user.UpdatedAt = time.Now().Unix()
|
||||
|
||||
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
|
||||
if u, _ := p.GetUserByPhone(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
|
||||
return user, fmt.Errorf("user with given phone number already exists")
|
||||
}
|
||||
}
|
||||
|
||||
result := p.db.Save(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
|
@ -58,13 +73,12 @@ func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.Use
|
|||
|
||||
// DeleteUser to delete user information from database
|
||||
func (p *provider) DeleteUser(ctx context.Context, user models.User) error {
|
||||
result := p.db.Delete(&user)
|
||||
|
||||
result := p.db.Where("user_id = ?", user.ID).Delete(&models.Session{})
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = p.db.Where("user_id = ?", user.ID).Delete(&models.Session{})
|
||||
result = p.db.Delete(&user)
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
@ -141,3 +155,14 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *provider) GetUserByPhone(ctx context.Context, phoneNumber string) (*models.User, error) {
|
||||
var user *models.User
|
||||
result := p.db.Where("phone_number = ?", phoneNumber).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
|
2
server/env/env.go
vendored
2
server/env/env.go
vendored
|
@ -332,7 +332,7 @@ func InitAllEnv() error {
|
|||
envData[constants.EnvKeyJwtRoleClaim] = osJwtRoleClaim
|
||||
|
||||
if envData[constants.EnvKeyJwtRoleClaim] == "" {
|
||||
envData[constants.EnvKeyJwtRoleClaim] = "role"
|
||||
envData[constants.EnvKeyJwtRoleClaim] = "roles"
|
||||
}
|
||||
}
|
||||
if osJwtRoleClaim != "" && envData[constants.EnvKeyJwtRoleClaim] != osJwtRoleClaim {
|
||||
|
|
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/coreos/go-oidc/v3 v3.1.0
|
||||
github.com/couchbase/gocb/v2 v2.5.3 // indirect
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/glebarez/sqlite v1.5.0
|
||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.0
|
||||
github.com/goccy/go-json v0.9.11 // indirect
|
||||
|
@ -25,18 +26,15 @@ require (
|
|||
github.com/stretchr/testify v1.8.0
|
||||
github.com/vektah/gqlparser/v2 v2.5.1
|
||||
go.mongodb.org/mongo-driver v1.8.1
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be
|
||||
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b // indirect
|
||||
golang.org/x/crypto v0.3.0
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
|
||||
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/square/go-jose.v2 v2.6.0
|
||||
gorm.io/driver/mysql v1.2.1
|
||||
gorm.io/driver/postgres v1.2.3
|
||||
gorm.io/driver/sqlite v1.2.6
|
||||
gorm.io/driver/sqlserver v1.2.1
|
||||
gorm.io/gorm v1.22.4
|
||||
gorm.io/driver/mysql v1.4.3
|
||||
gorm.io/driver/postgres v1.4.5
|
||||
gorm.io/driver/sqlserver v1.4.1
|
||||
gorm.io/gorm v1.24.1
|
||||
)
|
||||
|
|
137
server/go.sum
137
server/go.sum
|
@ -33,6 +33,10 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
|||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/99designs/gqlgen v0.17.20 h1:O7WzccIhKB1dm+7g6dhQcULINftfiLSBg2l/mwbpJMw=
|
||||
github.com/99designs/gqlgen v0.17.20/go.mod h1:Mja2HI23kWT1VRH09hvWshFgOzKswpO20o4ScpJIES4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
|
@ -86,12 +90,13 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL/7ND8HI=
|
||||
github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
|
||||
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
|
@ -103,6 +108,10 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
|||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/glebarez/go-sqlite v1.19.1 h1:o2XhjyR8CQ2m84+bVz10G0cabmG0tY4sIMiCbrcUTrY=
|
||||
github.com/glebarez/go-sqlite v1.19.1/go.mod h1:9AykawGIyIcxoSfpYWiX1SgTNHTNsa/FVc75cDkbp4M=
|
||||
github.com/glebarez/sqlite v1.5.0 h1:+8LAEpmywqresSoGlqjjT+I9m4PseIM3NcerIJ/V7mk=
|
||||
github.com/glebarez/sqlite v1.5.0/go.mod h1:0wzXzTvfVJIN2GqRhCdMbnYd+m+aH5/QV7B30rM6NgY=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
|
@ -131,10 +140,15 @@ github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJr
|
|||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -177,6 +191,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
|
@ -219,8 +234,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
|
|||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
|
||||
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
|
||||
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
|
||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
|
@ -237,31 +252,31 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
|
|||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
|
||||
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||
github.com/jackc/pgtype v1.9.0 h1:/SH1RxEtltvJgsDqp3TbiTFApD3mey3iygpuEGeuBXk=
|
||||
github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
|
||||
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.14.0 h1:TgdrmgnM7VY72EuSQzBbBd4JA1RLqJolrw9nQVZABVc=
|
||||
github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
|
||||
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
|
||||
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
|
||||
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
|
@ -272,6 +287,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
|
@ -287,6 +303,7 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
|
@ -305,8 +322,9 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
|||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
||||
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
||||
github.com/mitchellh/mapstructure v1.3.1 h1:cCBH2gTD2K0OtLlv/Y5H01VQCqmlDxz30kS5Y5bqfLA=
|
||||
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -314,7 +332,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
|
@ -328,6 +348,8 @@ github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7
|
|||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
@ -335,6 +357,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f h1:a7clxaGmmqtdNTXyvrp/lVO/Gnkzlhc/+dLs5v965GM=
|
||||
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f/go.mod h1:/mK7FZ3mFYEn9zvNPhpngTyatyehSwte5bJZ4ehL5Xw=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
@ -417,7 +441,6 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
@ -430,8 +453,12 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A=
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -495,6 +522,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
|||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
|
@ -502,12 +530,15 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas=
|
||||
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -566,12 +597,16 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -580,10 +615,13 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -594,6 +632,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -644,6 +684,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
|||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
|
@ -757,6 +798,7 @@ gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI
|
|||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -767,18 +809,17 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.2.1 h1:h+3f1l9Ng2C072Y2tIiLgPpWN78r1KXL7bHJ0nTjlhU=
|
||||
gorm.io/driver/mysql v1.2.1/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo=
|
||||
gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To=
|
||||
gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs=
|
||||
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
|
||||
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
|
||||
gorm.io/driver/sqlserver v1.2.1 h1:KhGOjvPX7JZ5hPyQICTJfMuTz88zgJ2lk9bWiHVNHd8=
|
||||
gorm.io/driver/sqlserver v1.2.1/go.mod h1:nixq0OB3iLXZDiPv6JSOjWuPgpyaRpOIIevYtA4Ulb4=
|
||||
gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
gorm.io/gorm v1.22.4 h1:8aPcyEJhY0MAt8aY6Dc524Pn+pO29K+ydu+e/cXSpQM=
|
||||
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
|
||||
gorm.io/driver/mysql v1.4.3 h1:/JhWJhO2v17d8hjApTltKNADm7K7YI2ogkR7avJUL3k=
|
||||
gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
|
||||
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
|
||||
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
|
||||
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
||||
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.1 h1:CgvzRniUdG67hBAzsxDGOAuq4Te1osVMYsa1eQbd4fs=
|
||||
gorm.io/gorm v1.24.1/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -786,6 +827,38 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
|
||||
modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0=
|
||||
modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
|
||||
modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA=
|
||||
modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0=
|
||||
modernc.org/libc v1.19.0 h1:bXyVhGQg6KIClTr8FMVIDPl7jtbcs7aS5WP7vLDaxPs=
|
||||
modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
|
||||
modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
|
||||
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.19.1 h1:8xmS5oLnZtAK//vnd4aTVj8VOeTAccEFOtUnIzfSw+4=
|
||||
modernc.org/sqlite v1.19.1/go.mod h1:UfQ83woKMaPW/ZBruK0T7YaFCrI+IE0LeWVY6pmnVms=
|
||||
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.14.0/go.mod h1:gQ7c1YPMvryCHCcmf8acB6VPabE59QBeuRQLL7cTUlM=
|
||||
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.6.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
|
|
@ -2250,6 +2250,10 @@ input SignUpInput {
|
|||
scope: [String!]
|
||||
redirect_uri: String
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
|
@ -2257,15 +2261,27 @@ input LoginInput {
|
|||
password: String!
|
||||
roles: [String!]
|
||||
scope: [String!]
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input VerifyEmailInput {
|
||||
token: String!
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input ResendVerifyEmailInput {
|
||||
email: String!
|
||||
identifier: String!
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input UpdateProfileInput {
|
||||
|
@ -2395,7 +2411,7 @@ input AddEmailTemplateRequest {
|
|||
event_name: String!
|
||||
subject: String!
|
||||
template: String!
|
||||
design: String!
|
||||
design: String
|
||||
}
|
||||
|
||||
input UpdateEmailTemplateRequest {
|
||||
|
@ -2413,10 +2429,18 @@ input DeleteEmailTemplateRequest {
|
|||
input VerifyOTPRequest {
|
||||
email: String!
|
||||
otp: String!
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input ResendOTPRequest {
|
||||
email: String!
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
|
@ -14130,7 +14154,7 @@ func (ec *executionContext) unmarshalInputAddEmailTemplateRequest(ctx context.Co
|
|||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("design"))
|
||||
it.Design, err = ec.unmarshalNString2string(ctx, v)
|
||||
it.Design, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
|
@ -14455,7 +14479,7 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
|
|||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"email", "password", "roles", "scope"}
|
||||
fieldsInOrder := [...]string{"email", "password", "roles", "scope", "state"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
|
@ -14494,6 +14518,14 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "state":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14659,7 +14691,7 @@ func (ec *executionContext) unmarshalInputResendOTPRequest(ctx context.Context,
|
|||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"email"}
|
||||
fieldsInOrder := [...]string{"email", "state"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
|
@ -14674,6 +14706,14 @@ func (ec *executionContext) unmarshalInputResendOTPRequest(ctx context.Context,
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "state":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14687,7 +14727,7 @@ func (ec *executionContext) unmarshalInputResendVerifyEmailInput(ctx context.Con
|
|||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"email", "identifier"}
|
||||
fieldsInOrder := [...]string{"email", "identifier", "state"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
|
@ -14710,6 +14750,14 @@ func (ec *executionContext) unmarshalInputResendVerifyEmailInput(ctx context.Con
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "state":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14803,7 +14851,7 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
|
|||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "password", "confirm_password", "roles", "scope", "redirect_uri", "is_multi_factor_auth_enabled"}
|
||||
fieldsInOrder := [...]string{"email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "password", "confirm_password", "roles", "scope", "redirect_uri", "is_multi_factor_auth_enabled", "state"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
|
@ -14930,6 +14978,14 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "state":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15815,7 +15871,7 @@ func (ec *executionContext) unmarshalInputVerifyEmailInput(ctx context.Context,
|
|||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"token"}
|
||||
fieldsInOrder := [...]string{"token", "state"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
|
@ -15830,6 +15886,14 @@ func (ec *executionContext) unmarshalInputVerifyEmailInput(ctx context.Context,
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "state":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15843,7 +15907,7 @@ func (ec *executionContext) unmarshalInputVerifyOTPRequest(ctx context.Context,
|
|||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"email", "otp"}
|
||||
fieldsInOrder := [...]string{"email", "otp", "state"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
|
@ -15866,6 +15930,14 @@ func (ec *executionContext) unmarshalInputVerifyOTPRequest(ctx context.Context,
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "state":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ type AddEmailTemplateRequest struct {
|
|||
EventName string `json:"event_name"`
|
||||
Subject string `json:"subject"`
|
||||
Template string `json:"template"`
|
||||
Design string `json:"design"`
|
||||
Design *string `json:"design"`
|
||||
}
|
||||
|
||||
type AddWebhookRequest struct {
|
||||
|
@ -151,6 +151,7 @@ type LoginInput struct {
|
|||
Password string `json:"password"`
|
||||
Roles []string `json:"roles"`
|
||||
Scope []string `json:"scope"`
|
||||
State *string `json:"state"`
|
||||
}
|
||||
|
||||
type MagicLinkLoginInput struct {
|
||||
|
@ -200,11 +201,13 @@ type PaginationInput struct {
|
|||
|
||||
type ResendOTPRequest struct {
|
||||
Email string `json:"email"`
|
||||
State *string `json:"state"`
|
||||
}
|
||||
|
||||
type ResendVerifyEmailInput struct {
|
||||
Email string `json:"email"`
|
||||
Identifier string `json:"identifier"`
|
||||
State *string `json:"state"`
|
||||
}
|
||||
|
||||
type ResetPasswordInput struct {
|
||||
|
@ -238,6 +241,7 @@ type SignUpInput struct {
|
|||
Scope []string `json:"scope"`
|
||||
RedirectURI *string `json:"redirect_uri"`
|
||||
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
|
||||
State *string `json:"state"`
|
||||
}
|
||||
|
||||
type TestEndpointRequest struct {
|
||||
|
@ -409,11 +413,13 @@ type VerificationRequests struct {
|
|||
|
||||
type VerifyEmailInput struct {
|
||||
Token string `json:"token"`
|
||||
State *string `json:"state"`
|
||||
}
|
||||
|
||||
type VerifyOTPRequest struct {
|
||||
Email string `json:"email"`
|
||||
Otp string `json:"otp"`
|
||||
State *string `json:"state"`
|
||||
}
|
||||
|
||||
type Webhook struct {
|
||||
|
|
|
@ -285,6 +285,10 @@ input SignUpInput {
|
|||
scope: [String!]
|
||||
redirect_uri: String
|
||||
is_multi_factor_auth_enabled: Boolean
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input LoginInput {
|
||||
|
@ -292,15 +296,27 @@ input LoginInput {
|
|||
password: String!
|
||||
roles: [String!]
|
||||
scope: [String!]
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input VerifyEmailInput {
|
||||
token: String!
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input ResendVerifyEmailInput {
|
||||
email: String!
|
||||
identifier: String!
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input UpdateProfileInput {
|
||||
|
@ -430,7 +446,9 @@ input AddEmailTemplateRequest {
|
|||
event_name: String!
|
||||
subject: String!
|
||||
template: String!
|
||||
design: String!
|
||||
# Design value is set when editor is used
|
||||
# If raw HTML is used design value is set to null
|
||||
design: String
|
||||
}
|
||||
|
||||
input UpdateEmailTemplateRequest {
|
||||
|
@ -438,6 +456,8 @@ input UpdateEmailTemplateRequest {
|
|||
event_name: String
|
||||
template: String
|
||||
subject: String
|
||||
# Design value is set when editor is used
|
||||
# If raw HTML is used design value is set to null
|
||||
design: String
|
||||
}
|
||||
|
||||
|
@ -448,10 +468,18 @@ input DeleteEmailTemplateRequest {
|
|||
input VerifyOTPRequest {
|
||||
email: String!
|
||||
otp: String!
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
input ResendOTPRequest {
|
||||
email: String!
|
||||
# state is used for authorization code grant flow
|
||||
# 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
|
||||
state: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
|
|
|
@ -1,10 +1,41 @@
|
|||
package handlers
|
||||
|
||||
/**
|
||||
LOGIC TO REMEMBER THE AUTHORIZE FLOW
|
||||
|
||||
|
||||
jargons
|
||||
`at_hash` -> access_token_hash
|
||||
`c_hash` -> code_hash
|
||||
|
||||
|
||||
# ResponseType: Code
|
||||
with /authorize request
|
||||
- set state [state, code@@challenge]
|
||||
- add &code to login redirect url
|
||||
login resolver has optional param state
|
||||
-if state found in store, split with @@
|
||||
- if len > 1 -> response type is code and has code + challenge
|
||||
- set `nonce, code` for createAuthToken request so that `c_hash` can be generated
|
||||
- do not add `nonce` to id_token in code flow, instead set `c_hash` and `at_hash`
|
||||
|
||||
|
||||
# ResponseType: token / id_token
|
||||
with /authorize request
|
||||
- set state [state, nonce]
|
||||
- add &nonce to login redirect url
|
||||
login resolver has optional param state
|
||||
- if state found in store, split with @@
|
||||
- if len < 1 -> response type is token / id_token and value is nonce
|
||||
- send received nonce for createAuthToken with empty code value
|
||||
- set `nonce` and `at_hash` in `id_token`
|
||||
**/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
@ -17,6 +48,15 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/token"
|
||||
)
|
||||
|
||||
// Check the flow for generating and verifying codes: https://developer.okta.com/blog/2019/08/22/okta-authjs-pkce#:~:text=PKCE%20works%20by%20having%20the,is%20called%20the%20Code%20Challenge.
|
||||
|
||||
// Check following docs for understanding request / response params for various types of requests: https://auth0.com/docs/authenticate/login/oidc-conformant-authentication/oidc-adoption-auth-code-flow
|
||||
|
||||
const (
|
||||
authorizeWebMessageTemplate = "authorize_web_message.tmpl"
|
||||
authorizeFormPostTemplate = "authorize_form_post.tmpl"
|
||||
)
|
||||
|
||||
// AuthorizeHandler is the handler for the /authorize route
|
||||
// required params
|
||||
// ?redirect_uri = redirect url
|
||||
|
@ -24,8 +64,6 @@ import (
|
|||
// state[recommended] = to prevent CSRF attack (for authorizer its compulsory)
|
||||
// code_challenge = to prevent CSRF attack
|
||||
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
|
||||
|
||||
// check the flow for generating and verifying codes: https://developer.okta.com/blog/2019/08/22/okta-authjs-pkce#:~:text=PKCE%20works%20by%20having%20the,is%20called%20the%20Code%20Challenge.
|
||||
func AuthorizeHandler() gin.HandlerFunc {
|
||||
return func(gc *gin.Context) {
|
||||
redirectURI := strings.TrimSpace(gc.Query("redirect_uri"))
|
||||
|
@ -34,8 +72,8 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
codeChallenge := strings.TrimSpace(gc.Query("code_challenge"))
|
||||
scopeString := strings.TrimSpace(gc.Query("scope"))
|
||||
clientID := strings.TrimSpace(gc.Query("client_id"))
|
||||
template := "authorize.tmpl"
|
||||
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
||||
nonce := strings.TrimSpace(gc.Query("nonce"))
|
||||
|
||||
var scope []string
|
||||
if scopeString == "" {
|
||||
|
@ -45,176 +83,97 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
}
|
||||
|
||||
if responseMode == "" {
|
||||
responseMode = "query"
|
||||
}
|
||||
|
||||
if responseMode != "query" && responseMode != "web_message" {
|
||||
log.Debug("Invalid response_mode: ", responseMode)
|
||||
gc.JSON(400, gin.H{"error": "invalid response mode"})
|
||||
responseMode = constants.ResponseModeQuery
|
||||
}
|
||||
|
||||
if redirectURI == "" {
|
||||
redirectURI = "/app"
|
||||
}
|
||||
|
||||
isQuery := responseMode == "query"
|
||||
|
||||
loginURL := "/app?state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
||||
|
||||
if clientID == "" {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
log.Debug("Failed to get client_id: ", clientID)
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "client_id is required",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if client, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID); client != clientID || err != nil {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
log.Debug("Invalid client_id: ", clientID)
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "invalid_client_id",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if state == "" {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
log.Debug("Failed to get state: ", state)
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "state is required",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if responseType == "" {
|
||||
responseType = "token"
|
||||
}
|
||||
|
||||
isResponseTypeCode := responseType == "code"
|
||||
isResponseTypeToken := responseType == "token"
|
||||
|
||||
if !isResponseTypeCode && !isResponseTypeToken {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
log.Debug("Invalid response_type: ", responseType)
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "response_type is invalid",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
if err := validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge); err != nil {
|
||||
log.Debug("invalid authorization request: ", err)
|
||||
gc.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if isResponseTypeCode {
|
||||
if codeChallenge == "" {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
log.Debug("Failed to get code_challenge: ", codeChallenge)
|
||||
gc.HTML(http.StatusBadRequest, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "code_challenge is required",
|
||||
},
|
||||
},
|
||||
})
|
||||
code := uuid.New().String()
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
return
|
||||
|
||||
log := log.WithFields(log.Fields{
|
||||
"response_mode": responseMode,
|
||||
"response_type": responseType,
|
||||
})
|
||||
|
||||
// TODO add state with timeout
|
||||
// used for response mode query or fragment
|
||||
loginState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI
|
||||
if responseType == constants.ResponseTypeCode {
|
||||
loginState += "&code=" + code
|
||||
if err := memorystore.Provider.SetState(state, code+"@@"+codeChallenge); err != nil {
|
||||
log.Debug("Error setting temp code", err)
|
||||
}
|
||||
} else {
|
||||
loginState += "&nonce=" + nonce
|
||||
if err := memorystore.Provider.SetState(state, nonce); err != nil {
|
||||
log.Debug("Error setting temp code", err)
|
||||
}
|
||||
}
|
||||
|
||||
sessionToken, err := cookie.GetSession(gc)
|
||||
if err != nil {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
loginURL := "/app?" + loginState
|
||||
|
||||
if responseMode == constants.ResponseModeFragment {
|
||||
loginURL = "/app#" + loginState
|
||||
}
|
||||
|
||||
if responseType == constants.ResponseTypeCode && codeChallenge == "" {
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"response": map[string]interface{}{
|
||||
"error": "code_challenge_required",
|
||||
"error_description": "code challenge is required",
|
||||
},
|
||||
}, http.StatusOK)
|
||||
}
|
||||
|
||||
loginError := map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]interface{}{
|
||||
"error": "login_required",
|
||||
"error_description": "Login is required",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
sessionToken, err := cookie.GetSession(gc)
|
||||
if err != nil {
|
||||
log.Debug("GetSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
// get session from cookie
|
||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||
if err != nil {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "login_required",
|
||||
"error_description": "Login is required",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
log.Debug("ValidateBrowserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
userID := claims.Subject
|
||||
user, err := db.Provider.GetUserByID(gc, userID)
|
||||
if err != nil {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
log.Debug("GetUserByID failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"response": map[string]interface{}{
|
||||
"error": "signup_required",
|
||||
"error_description": "Sign up required",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -223,81 +182,102 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
sessionKey = claims.LoginMethod + ":" + user.ID
|
||||
}
|
||||
|
||||
// if user is logged in
|
||||
// based on the response type code, generate the response
|
||||
if isResponseTypeCode {
|
||||
// rollover the session for security
|
||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
|
||||
nonce := uuid.New().String()
|
||||
if responseType == constants.ResponseTypeCode {
|
||||
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
|
||||
if err != nil {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "login_required",
|
||||
"error_description": "Login is required",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
log.Debug("CreateSessionToken failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: add state with timeout
|
||||
// if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
|
||||
// log.Debug("SetState failed: ", err)
|
||||
// handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
// return
|
||||
// }
|
||||
|
||||
// TODO: add state with timeout
|
||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+newSessionToken); err != nil {
|
||||
log.Debug("SetState failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken); err != nil {
|
||||
log.Debug("SetUserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken)
|
||||
cookie.SetSession(gc, newSessionToken)
|
||||
code := uuid.New().String()
|
||||
memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken)
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
|
||||
// in case, response type is code and user is already logged in send the code and state
|
||||
// and cookie session will already be rolled over and set
|
||||
// gc.HTML(http.StatusOK, authorizeWebMessageTemplate, gin.H{
|
||||
// "target_origin": redirectURI,
|
||||
// "authorization_response": map[string]interface{}{
|
||||
// "type": "authorization_response",
|
||||
// "response": map[string]string{
|
||||
// "code": code,
|
||||
// "state": state,
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
|
||||
params := "code=" + code + "&state=" + state + "&nonce=" + nonce
|
||||
if responseMode == constants.ResponseModeQuery {
|
||||
if strings.Contains(redirectURI, "?") {
|
||||
redirectURI = redirectURI + "&" + params
|
||||
} else {
|
||||
redirectURI = redirectURI + "?" + params
|
||||
}
|
||||
} else if responseMode == constants.ResponseModeFragment {
|
||||
if strings.Contains(redirectURI, "#") {
|
||||
redirectURI = redirectURI + "&" + params
|
||||
} else {
|
||||
redirectURI = redirectURI + "#" + params
|
||||
}
|
||||
}
|
||||
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"response": map[string]interface{}{
|
||||
"code": code,
|
||||
"state": state,
|
||||
},
|
||||
},
|
||||
})
|
||||
}, http.StatusOK)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if isResponseTypeToken {
|
||||
if responseType == constants.ResponseTypeToken || responseType == constants.ResponseTypeIDToken {
|
||||
// rollover the session for security
|
||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod)
|
||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod, nonce, "")
|
||||
if err != nil {
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "login_required",
|
||||
"error_description": "Login is required",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
log.Debug("CreateAuthToken failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash); err != nil {
|
||||
log.Debug("SetUserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.FingerPrintHash); err != nil {
|
||||
log.Debug("SetUserSession failed: ", err)
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if expiresIn <= 0 {
|
||||
expiresIn = 1
|
||||
}
|
||||
|
||||
// used of query mode
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(authToken.IDToken.ExpiresAt, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||
|
||||
res := map[string]interface{}{
|
||||
"access_token": authToken.AccessToken.Token,
|
||||
|
@ -305,7 +285,12 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
"state": state,
|
||||
"scope": scope,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": expiresIn,
|
||||
"expires_in": authToken.AccessToken.ExpiresAt,
|
||||
}
|
||||
|
||||
if nonce != "" {
|
||||
params += "&nonce=" + nonce
|
||||
res["nonce"] = nonce
|
||||
}
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
|
@ -314,38 +299,77 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
if isQuery {
|
||||
if responseMode == constants.ResponseModeQuery {
|
||||
if strings.Contains(redirectURI, "?") {
|
||||
gc.Redirect(http.StatusFound, redirectURI+"&"+params)
|
||||
redirectURI = redirectURI + "&" + params
|
||||
} else {
|
||||
gc.Redirect(http.StatusFound, redirectURI+"?"+params)
|
||||
redirectURI = redirectURI + "?" + params
|
||||
}
|
||||
} else if responseMode == constants.ResponseModeFragment {
|
||||
if strings.Contains(redirectURI, "#") {
|
||||
redirectURI = redirectURI + "&" + params
|
||||
} else {
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
redirectURI = redirectURI + "#" + params
|
||||
}
|
||||
}
|
||||
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": res,
|
||||
},
|
||||
})
|
||||
}
|
||||
}, http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
if isQuery {
|
||||
gc.Redirect(http.StatusFound, loginURL)
|
||||
} else {
|
||||
// by default return with error
|
||||
gc.HTML(http.StatusOK, template, gin.H{
|
||||
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge string) error {
|
||||
if strings.TrimSpace(state) == "" {
|
||||
return fmt.Errorf("invalid state. state is required to prevent csrf attack", responseMode)
|
||||
}
|
||||
if responseType != constants.ResponseTypeCode && responseType != constants.ResponseTypeToken && responseType != constants.ResponseTypeIDToken {
|
||||
return fmt.Errorf("invalid response type %s. 'code' & 'token' are valid response_type", responseMode)
|
||||
}
|
||||
|
||||
if responseMode != constants.ResponseModeQuery && responseMode != constants.ResponseModeWebMessage && responseMode != constants.ResponseModeFragment && responseMode != constants.ResponseModeFormPost {
|
||||
return fmt.Errorf("invalid response mode %s. 'query', 'fragment', 'form_post' and 'web_message' are valid response_mode", responseMode)
|
||||
}
|
||||
|
||||
if client, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID); client != clientID || err != nil {
|
||||
return fmt.Errorf("invalid client_id %s", clientID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleResponse(gc *gin.Context, responseMode, loginURI, redirectURI string, data map[string]interface{}, httpStatusCode int) {
|
||||
isAuthenticationRequired := false
|
||||
if _, ok := data["response"].(map[string]interface{})["error"]; ok {
|
||||
isAuthenticationRequired = true
|
||||
}
|
||||
|
||||
if isAuthenticationRequired {
|
||||
gc.Redirect(http.StatusFound, loginURI)
|
||||
return
|
||||
}
|
||||
|
||||
switch responseMode {
|
||||
case constants.ResponseModeQuery, constants.ResponseModeFragment:
|
||||
|
||||
gc.Redirect(http.StatusFound, redirectURI)
|
||||
return
|
||||
case constants.ResponseModeWebMessage:
|
||||
gc.HTML(httpStatusCode, authorizeWebMessageTemplate, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": map[string]interface{}{
|
||||
"type": "authorization_response",
|
||||
"response": map[string]string{
|
||||
"error": "login_required",
|
||||
"error_description": "Login is required",
|
||||
},
|
||||
},
|
||||
"authorization_response": data,
|
||||
})
|
||||
}
|
||||
return
|
||||
case constants.ResponseModeFormPost:
|
||||
gc.HTML(httpStatusCode, authorizeFormPostTemplate, gin.H{
|
||||
"target_origin": redirectURI,
|
||||
"authorization_response": data["response"],
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
|
@ -55,20 +56,20 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||
scopes := strings.Split(sessionSplit[3], ",")
|
||||
|
||||
user := models.User{}
|
||||
code := ctx.Request.FormValue("code")
|
||||
oauthCode := ctx.Request.FormValue("code")
|
||||
switch provider {
|
||||
case constants.AuthRecipeMethodGoogle:
|
||||
user, err = processGoogleUserInfo(code)
|
||||
user, err = processGoogleUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodGithub:
|
||||
user, err = processGithubUserInfo(code)
|
||||
user, err = processGithubUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodFacebook:
|
||||
user, err = processFacebookUserInfo(code)
|
||||
user, err = processFacebookUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodLinkedIn:
|
||||
user, err = processLinkedInUserInfo(code)
|
||||
user, err = processLinkedInUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodApple:
|
||||
user, err = processAppleUserInfo(code)
|
||||
user, err = processAppleUserInfo(oauthCode)
|
||||
case constants.AuthRecipeMethodTwitter:
|
||||
user, err = processTwitterUserInfo(code, sessionState)
|
||||
user, err = processTwitterUserInfo(oauthCode, sessionState)
|
||||
default:
|
||||
log.Info("Invalid oauth provider")
|
||||
err = fmt.Errorf(`invalid oauth provider`)
|
||||
|
@ -196,18 +197,53 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(ctx, user, inputRoles, scopes, provider)
|
||||
// TODO
|
||||
// use stateValue to get code / nonce
|
||||
// add code / nonce to id_token
|
||||
code := ""
|
||||
codeChallenge := ""
|
||||
nonce := ""
|
||||
if stateValue != "" {
|
||||
// Get state from store
|
||||
authorizeState, _ := memorystore.Provider.GetState(stateValue)
|
||||
if authorizeState != "" {
|
||||
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||
if len(authorizeStateSplit) > 1 {
|
||||
code = authorizeStateSplit[0]
|
||||
codeChallenge = authorizeStateSplit[1]
|
||||
} else {
|
||||
nonce = authorizeState
|
||||
}
|
||||
go memorystore.Provider.RemoveState(stateValue)
|
||||
}
|
||||
}
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
authToken, err := token.CreateAuthToken(ctx, user, inputRoles, scopes, provider, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token: ", err)
|
||||
ctx.JSON(500, gin.H{"error": err.Error()})
|
||||
}
|
||||
|
||||
// Code challenge could be optional if PKCE flow is not used
|
||||
if code != "" {
|
||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||
log.Debug("SetState failed: ", err)
|
||||
ctx.JSON(500, gin.H{"error": err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if expiresIn <= 0 {
|
||||
expiresIn = 1
|
||||
}
|
||||
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token + "&nonce=" + nonce
|
||||
|
||||
if code != "" {
|
||||
params += "&code=" + code
|
||||
}
|
||||
|
||||
sessionKey := provider + ":" + user.ID
|
||||
cookie.SetSession(ctx, authToken.FingerPrintHash)
|
||||
|
@ -215,7 +251,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
params = params + `&refresh_token=` + authToken.RefreshToken.Token
|
||||
params += `&refresh_token=` + authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
|
@ -17,12 +18,22 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/token"
|
||||
)
|
||||
|
||||
type RequestBody struct {
|
||||
CodeVerifier string `form:"code_verifier" json:"code_verifier"`
|
||||
Code string `form:"code" json:"code"`
|
||||
ClientID string `form:"client_id" json:"client_id"`
|
||||
ClientSecret string `form:"client_secret" json:"client_secret"`
|
||||
GrantType string `form:"grant_type" json:"grant_type"`
|
||||
RefreshToken string `form:"refresh_token" json:"refresh_token"`
|
||||
RedirectURI string `form:"redirect_uri" json:"redirect_uri"`
|
||||
}
|
||||
|
||||
// TokenHandler to handle /oauth/token requests
|
||||
// grant type required
|
||||
func TokenHandler() gin.HandlerFunc {
|
||||
return func(gc *gin.Context) {
|
||||
var reqBody map[string]string
|
||||
if err := gc.BindJSON(&reqBody); err != nil {
|
||||
var reqBody RequestBody
|
||||
if err := gc.Bind(&reqBody); err != nil {
|
||||
log.Debug("Error binding JSON: ", err)
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "error_binding_json",
|
||||
|
@ -31,11 +42,12 @@ func TokenHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
codeVerifier := strings.TrimSpace(reqBody["code_verifier"])
|
||||
code := strings.TrimSpace(reqBody["code"])
|
||||
clientID := strings.TrimSpace(reqBody["client_id"])
|
||||
grantType := strings.TrimSpace(reqBody["grant_type"])
|
||||
refreshToken := strings.TrimSpace(reqBody["refresh_token"])
|
||||
codeVerifier := strings.TrimSpace(reqBody.CodeVerifier)
|
||||
code := strings.TrimSpace(reqBody.Code)
|
||||
clientID := strings.TrimSpace(reqBody.ClientID)
|
||||
grantType := strings.TrimSpace(reqBody.GrantType)
|
||||
refreshToken := strings.TrimSpace(reqBody.RefreshToken)
|
||||
clientSecret := strings.TrimSpace(reqBody.ClientSecret)
|
||||
|
||||
if grantType == "" {
|
||||
grantType = "authorization_code"
|
||||
|
@ -52,6 +64,12 @@ func TokenHandler() gin.HandlerFunc {
|
|||
})
|
||||
}
|
||||
|
||||
// check if clientID & clientSecret are present as part of
|
||||
// authorization header with basic auth
|
||||
if clientID == "" && clientSecret == "" {
|
||||
clientID, clientSecret, _ = gc.Request.BasicAuth()
|
||||
}
|
||||
|
||||
if clientID == "" {
|
||||
log.Debug("Client ID is empty")
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
|
@ -76,15 +94,6 @@ func TokenHandler() gin.HandlerFunc {
|
|||
sessionKey := ""
|
||||
|
||||
if isAuthorizationCodeGrant {
|
||||
if codeVerifier == "" {
|
||||
log.Debug("Code verifier is empty")
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "invalid_code_verifier",
|
||||
"error_description": "The code verifier is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if code == "" {
|
||||
log.Debug("Code is empty")
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
|
@ -94,14 +103,37 @@ func TokenHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(codeVerifier))
|
||||
encryptedCode := strings.ReplaceAll(base64.URLEncoding.EncodeToString(hash.Sum(nil)), "+", "-")
|
||||
encryptedCode = strings.ReplaceAll(encryptedCode, "/", "_")
|
||||
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
|
||||
sessionData, err := memorystore.Provider.GetState(encryptedCode)
|
||||
if codeVerifier == "" && clientSecret == "" {
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "invalid_dat",
|
||||
"error_description": "The code verifier or client secret is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
// Get state
|
||||
sessionData, err := memorystore.Provider.GetState(code)
|
||||
if sessionData == "" || err != nil {
|
||||
log.Debug("Session data is empty")
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "invalid_code",
|
||||
"error_description": "The code is invalid",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// [0] -> code_challenge
|
||||
// [1] -> session cookie
|
||||
sessionDataSplit := strings.Split(sessionData, "@@")
|
||||
|
||||
go memorystore.Provider.RemoveState(code)
|
||||
|
||||
if codeVerifier != "" {
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(codeVerifier))
|
||||
encryptedCode := strings.ReplaceAll(base64.RawURLEncoding.EncodeToString(hash.Sum(nil)), "+", "-")
|
||||
encryptedCode = strings.ReplaceAll(encryptedCode, "/", "_")
|
||||
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
|
||||
if encryptedCode != sessionDataSplit[0] {
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "invalid_code_verifier",
|
||||
"error_description": "The code verifier is invalid",
|
||||
|
@ -109,19 +141,16 @@ func TokenHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
go memorystore.Provider.RemoveState(encryptedCode)
|
||||
// split session data
|
||||
// it contains code@sessiontoken
|
||||
sessionDataSplit := strings.Split(sessionData, "@")
|
||||
|
||||
if sessionDataSplit[0] != code {
|
||||
log.Debug("Invalid code verifier. Unable to split session data")
|
||||
} else {
|
||||
if clientHash, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientSecret); clientSecret != clientHash || err != nil {
|
||||
log.Debug("Client Secret is invalid: ", clientID)
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "invalid_code_verifier",
|
||||
"error_description": "The code verifier is invalid",
|
||||
"error": "invalid_client_secret",
|
||||
"error_description": "The client secret is invalid",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// validate session
|
||||
claims, err := token.ValidateBrowserSession(gc, sessionDataSplit[1])
|
||||
|
@ -146,6 +175,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||
}
|
||||
|
||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
|
||||
|
||||
} else {
|
||||
// validate refresh token
|
||||
if refreshToken == "" {
|
||||
|
@ -154,6 +184,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||
"error": "invalid_refresh_token",
|
||||
"error_description": "The refresh token is invalid",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := token.ValidateRefreshToken(gc, refreshToken)
|
||||
|
@ -163,9 +194,10 @@ func TokenHandler() gin.HandlerFunc {
|
|||
"error": "unauthorized",
|
||||
"error_description": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
userID = claims["sub"].(string)
|
||||
loginMethod := claims["login_method"]
|
||||
claimLoginMethod := claims["login_method"]
|
||||
rolesInterface := claims["roles"].([]interface{})
|
||||
scopeInterface := claims["scope"].([]interface{})
|
||||
for _, v := range rolesInterface {
|
||||
|
@ -176,9 +208,11 @@ func TokenHandler() gin.HandlerFunc {
|
|||
}
|
||||
|
||||
sessionKey = userID
|
||||
if loginMethod != nil && loginMethod != "" {
|
||||
sessionKey = loginMethod.(string) + ":" + sessionKey
|
||||
if claimLoginMethod != nil && claimLoginMethod != "" {
|
||||
sessionKey = claimLoginMethod.(string) + ":" + sessionKey
|
||||
loginMethod = claimLoginMethod.(string)
|
||||
}
|
||||
|
||||
// remove older refresh token and rotate it for security
|
||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims["nonce"].(string))
|
||||
}
|
||||
|
@ -202,7 +236,8 @@ func TokenHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod)
|
||||
nonce := uuid.New().String() + "@@" + code
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Error creating auth token: ", err)
|
||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||
|
@ -211,6 +246,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||
})
|
||||
return
|
||||
}
|
||||
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
|
@ -98,7 +99,30 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||
if verificationRequest.Identifier == constants.VerificationTypeMagicLinkLogin {
|
||||
loginMethod = constants.AuthRecipeMethodMagicLinkLogin
|
||||
}
|
||||
authToken, err := token.CreateAuthToken(c, user, roles, scope, loginMethod)
|
||||
|
||||
code := ""
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// codeChallenge := ""
|
||||
nonce := ""
|
||||
if state != "" {
|
||||
// Get state from store
|
||||
authorizeState, _ := memorystore.Provider.GetState(state)
|
||||
if authorizeState != "" {
|
||||
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||
if len(authorizeStateSplit) > 1 {
|
||||
code = authorizeStateSplit[0]
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// codeChallenge = authorizeStateSplit[1]
|
||||
} else {
|
||||
nonce = authorizeState
|
||||
}
|
||||
go memorystore.Provider.RemoveState(state)
|
||||
}
|
||||
}
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
authToken, err := token.CreateAuthToken(c, user, roles, scope, loginMethod, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Error creating auth token: ", err)
|
||||
errorRes["error_description"] = err.Error()
|
||||
|
@ -106,12 +130,27 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// Code challenge could be optional if PKCE flow is not used
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// if code != "" {
|
||||
// if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||
// log.Debug("Error setting code state ", err)
|
||||
// errorRes["error_description"] = err.Error()
|
||||
// c.JSON(500, errorRes)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if expiresIn <= 0 {
|
||||
expiresIn = 1
|
||||
}
|
||||
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token + "&nonce=" + nonce
|
||||
|
||||
if code != "" {
|
||||
params += "&code=" + code
|
||||
}
|
||||
|
||||
sessionKey := loginMethod + ":" + user.ID
|
||||
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||
|
|
61
server/logs/logs.go
Normal file
61
server/logs/logs.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package logs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"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 {
|
||||
|
||||
// log instance for gin server
|
||||
log := logrus.New()
|
||||
log.SetFormatter(LogUTCFormatter{&logrus.JSONFormatter{}})
|
||||
|
||||
if cliLogLevel == "" {
|
||||
cliLogLevel = os.Getenv("LOG_LEVEL")
|
||||
}
|
||||
|
||||
var logLevel logrus.Level
|
||||
switch cliLogLevel {
|
||||
case "debug":
|
||||
logLevel = logrus.DebugLevel
|
||||
case "info":
|
||||
logLevel = logrus.InfoLevel
|
||||
case "warn":
|
||||
logLevel = logrus.WarnLevel
|
||||
case "error":
|
||||
logLevel = logrus.ErrorLevel
|
||||
case "fatal":
|
||||
logLevel = logrus.FatalLevel
|
||||
case "panic":
|
||||
logLevel = logrus.PanicLevel
|
||||
default:
|
||||
logLevel = logrus.InfoLevel
|
||||
}
|
||||
// set log level globally
|
||||
logrus.SetLevel(logLevel)
|
||||
|
||||
// set log level for go-gin middleware
|
||||
log.SetLevel(logLevel)
|
||||
|
||||
// show file path in log for debug or other log levels.
|
||||
if logLevel != logrus.InfoLevel {
|
||||
logrus.SetReportCaller(true)
|
||||
log.SetReportCaller(true)
|
||||
}
|
||||
|
||||
return log
|
||||
}
|
|
@ -3,84 +3,42 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/cli"
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/env"
|
||||
"github.com/authorizerdev/authorizer/server/logs"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/oauth"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/routes"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// VERSION is used to define the version of authorizer from build tags
|
||||
var VERSION string
|
||||
|
||||
// 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 main() {
|
||||
cli.ARG_DB_URL = flag.String("database_url", "", "Database connection string")
|
||||
cli.ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
|
||||
cli.ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
|
||||
cli.ARG_LOG_LEVEL = flag.String("log_level", "info", "Log level, possible values are debug,info,warn,error,fatal,panic")
|
||||
cli.ARG_LOG_LEVEL = flag.String("log_level", "", "Log level, possible values are debug,info,warn,error,fatal,panic")
|
||||
cli.ARG_REDIS_URL = flag.String("redis_url", "", "Redis connection string")
|
||||
flag.Parse()
|
||||
|
||||
// global log level
|
||||
logrus.SetFormatter(LogUTCFormatter{&logrus.JSONFormatter{}})
|
||||
|
||||
// log instance for gin server
|
||||
log := logrus.New()
|
||||
log.SetFormatter(LogUTCFormatter{&logrus.JSONFormatter{}})
|
||||
|
||||
var logLevel logrus.Level
|
||||
switch *cli.ARG_LOG_LEVEL {
|
||||
case "debug":
|
||||
logLevel = logrus.DebugLevel
|
||||
case "info":
|
||||
logLevel = logrus.InfoLevel
|
||||
case "warn":
|
||||
logLevel = logrus.WarnLevel
|
||||
case "error":
|
||||
logLevel = logrus.ErrorLevel
|
||||
case "fatal":
|
||||
logLevel = logrus.FatalLevel
|
||||
case "panic":
|
||||
logLevel = logrus.PanicLevel
|
||||
default:
|
||||
logLevel = logrus.InfoLevel
|
||||
}
|
||||
// set log level globally
|
||||
logrus.SetLevel(logLevel)
|
||||
|
||||
// set log level for go-gin middleware
|
||||
log.SetLevel(logLevel)
|
||||
|
||||
// show file path in log for debug or other log levels.
|
||||
if logLevel != logrus.InfoLevel {
|
||||
logrus.SetReportCaller(true)
|
||||
log.SetReportCaller(true)
|
||||
}
|
||||
logrus.SetFormatter(logs.LogUTCFormatter{&logrus.JSONFormatter{}})
|
||||
|
||||
constants.VERSION = VERSION
|
||||
|
||||
// initialize required envs (mainly db, env file path and redis)
|
||||
err := memorystore.InitRequiredEnv()
|
||||
if err != nil {
|
||||
log.Fatal("Error while initializing required envs: ", err)
|
||||
logrus.Fatal("Error while initializing required envs: ", err)
|
||||
}
|
||||
|
||||
log := logs.InitLog(refs.StringValue(cli.ARG_LOG_LEVEL))
|
||||
|
||||
// initialize memory store
|
||||
err = memorystore.InitMemStore()
|
||||
if err != nil {
|
||||
|
|
|
@ -20,12 +20,12 @@ func GetHost(c *gin.Context) string {
|
|||
authorizerURL = ""
|
||||
}
|
||||
if authorizerURL != "" {
|
||||
return authorizerURL
|
||||
return strings.TrimSuffix(authorizerURL, "/")
|
||||
}
|
||||
|
||||
authorizerURL = c.Request.Header.Get("X-Authorizer-URL")
|
||||
if authorizerURL != "" {
|
||||
return authorizerURL
|
||||
return strings.TrimSuffix(authorizerURL, "/")
|
||||
}
|
||||
|
||||
scheme := c.Request.Header.Get("X-Forwarded-Proto")
|
||||
|
@ -33,7 +33,7 @@ func GetHost(c *gin.Context) string {
|
|||
scheme = "http"
|
||||
}
|
||||
host := c.Request.Host
|
||||
return scheme + "://" + host
|
||||
return strings.TrimSuffix(scheme+"://"+host, "/")
|
||||
}
|
||||
|
||||
// GetHostName function returns hostname and port
|
||||
|
@ -91,7 +91,7 @@ func GetDomainName(uri string) string {
|
|||
return host
|
||||
}
|
||||
|
||||
// GetAppURL to get /app/ url if not configured by user
|
||||
// GetAppURL to get /app url if not configured by user
|
||||
func GetAppURL(gc *gin.Context) string {
|
||||
envAppURL, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||
if envAppURL == "" || err != nil {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/validators"
|
||||
|
@ -40,15 +41,17 @@ func AddEmailTemplateResolver(ctx context.Context, params model.AddEmailTemplate
|
|||
return nil, fmt.Errorf("empty template not allowed")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(params.Design) == "" {
|
||||
return nil, fmt.Errorf("empty design not allowed")
|
||||
var design string
|
||||
|
||||
if params.Design == nil || strings.TrimSpace(refs.StringValue(params.Design)) == "" {
|
||||
design = ""
|
||||
}
|
||||
|
||||
_, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{
|
||||
EventName: params.EventName,
|
||||
Template: params.Template,
|
||||
Subject: params.Subject,
|
||||
Design: params.Design,
|
||||
Design: design,
|
||||
})
|
||||
if err != nil {
|
||||
log.Debug("Failed to add email template: ", err)
|
||||
|
|
|
@ -94,7 +94,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||
return res, err
|
||||
}
|
||||
|
||||
// exec it as go routine so that we can reduce the api latency
|
||||
// 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(),
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
|
@ -140,12 +141,43 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||
}, nil
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth)
|
||||
code := ""
|
||||
codeChallenge := ""
|
||||
nonce := ""
|
||||
if params.State != nil {
|
||||
// Get state from store
|
||||
authorizeState, _ := memorystore.Provider.GetState(refs.StringValue(params.State))
|
||||
if authorizeState != "" {
|
||||
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||
if len(authorizeStateSplit) > 1 {
|
||||
code = authorizeStateSplit[0]
|
||||
codeChallenge = authorizeStateSplit[1]
|
||||
} else {
|
||||
nonce = authorizeState
|
||||
}
|
||||
go memorystore.Provider.RemoveState(refs.StringValue(params.State))
|
||||
}
|
||||
}
|
||||
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// TODO add to other login options as well
|
||||
// Code challenge could be optional if PKCE flow is not used
|
||||
if code != "" {
|
||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||
log.Debug("SetState failed: ", err)
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if expiresIn <= 0 {
|
||||
expiresIn = 1
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/authorizerdev/authorizer/server/validators"
|
||||
|
@ -185,7 +186,7 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
|||
}
|
||||
redirectURLParams := "&roles=" + strings.Join(inputRoles, ",")
|
||||
if params.State != nil {
|
||||
redirectURLParams = redirectURLParams + "&state=" + *params.State
|
||||
redirectURLParams = redirectURLParams + "&state=" + refs.StringValue(params.State)
|
||||
}
|
||||
if params.Scope != nil && len(params.Scope) > 0 {
|
||||
redirectURLParams = redirectURLParams + "&scope=" + strings.Join(params.Scope, " ")
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
|
@ -70,7 +71,8 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
|||
scope = params.Scope
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, claimRoles, scope, claims.LoginMethod)
|
||||
nonce := uuid.New().String()
|
||||
authToken, err := token.CreateAuthToken(gc, user, claimRoles, scope, claims.LoginMethod, nonce, "")
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token: ", err)
|
||||
return res, err
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
|
@ -242,12 +243,42 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||
scope = params.Scope
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth)
|
||||
code := ""
|
||||
codeChallenge := ""
|
||||
nonce := ""
|
||||
if params.State != nil {
|
||||
// Get state from store
|
||||
authorizeState, _ := memorystore.Provider.GetState(refs.StringValue(params.State))
|
||||
if authorizeState != "" {
|
||||
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||
if len(authorizeStateSplit) > 1 {
|
||||
code = authorizeStateSplit[0]
|
||||
codeChallenge = authorizeStateSplit[1]
|
||||
} else {
|
||||
nonce = authorizeState
|
||||
}
|
||||
go memorystore.Provider.RemoveState(refs.StringValue(params.State))
|
||||
}
|
||||
}
|
||||
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token: ", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Code challenge could be optional if PKCE flow is not used
|
||||
if code != "" {
|
||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||
log.Debug("SetState failed: ", err)
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if expiresIn <= 0 {
|
||||
expiresIn = 1
|
||||
|
|
|
@ -125,7 +125,6 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
|||
return res, fmt.Errorf("user with this email address already exists")
|
||||
}
|
||||
|
||||
// TODO figure out how to do this
|
||||
go memorystore.Provider.DeleteAllUserSessions(user.ID)
|
||||
|
||||
hostname := parsers.GetHost(gc)
|
||||
|
|
|
@ -77,7 +77,16 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
|
|||
}
|
||||
}
|
||||
|
||||
claimRolesInterface := claims["roles"]
|
||||
claimKey := "roles"
|
||||
|
||||
if tokenType == constants.TokenTypeIdentityToken {
|
||||
claimKey, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)
|
||||
if err != nil {
|
||||
claimKey = "roles"
|
||||
}
|
||||
}
|
||||
|
||||
claimRolesInterface := claims[claimKey]
|
||||
roleSlice := utils.ConvertInterfaceToSlice(claimRolesInterface)
|
||||
for _, v := range roleSlice {
|
||||
claimRoles = append(claimRoles, v.(string))
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
@ -84,12 +86,42 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
|||
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod)
|
||||
code := ""
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// codeChallenge := ""
|
||||
nonce := ""
|
||||
if params.State != nil {
|
||||
// Get state from store
|
||||
authorizeState, _ := memorystore.Provider.GetState(refs.StringValue(params.State))
|
||||
if authorizeState != "" {
|
||||
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||
if len(authorizeStateSplit) > 1 {
|
||||
code = authorizeStateSplit[0]
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// codeChallenge = authorizeStateSplit[1]
|
||||
} else {
|
||||
nonce = authorizeState
|
||||
}
|
||||
go memorystore.Provider.RemoveState(refs.StringValue(params.State))
|
||||
}
|
||||
}
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token: ", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Code challenge could be optional if PKCE flow is not used
|
||||
// Not required as /oauth/token cannot be resumed from other tab
|
||||
// if code != "" {
|
||||
// if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||
// log.Debug("SetState failed: ", err)
|
||||
// return res, err
|
||||
// }
|
||||
// }
|
||||
go func() {
|
||||
if isSignUp {
|
||||
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, loginMethod, user)
|
||||
|
|
|
@ -12,8 +12,10 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/refs"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -57,12 +59,40 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod
|
|||
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod)
|
||||
code := ""
|
||||
codeChallenge := ""
|
||||
nonce := ""
|
||||
if params.State != nil {
|
||||
// Get state from store
|
||||
authorizeState, _ := memorystore.Provider.GetState(refs.StringValue(params.State))
|
||||
if authorizeState != "" {
|
||||
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||
if len(authorizeStateSplit) > 1 {
|
||||
code = authorizeStateSplit[0]
|
||||
codeChallenge = authorizeStateSplit[1]
|
||||
} else {
|
||||
nonce = authorizeState
|
||||
}
|
||||
go memorystore.Provider.RemoveState(refs.StringValue(params.State))
|
||||
}
|
||||
}
|
||||
if nonce == "" {
|
||||
nonce = uuid.New().String()
|
||||
}
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod, nonce, code)
|
||||
if err != nil {
|
||||
log.Debug("Failed to create auth token: ", err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Code challenge could be optional if PKCE flow is not used
|
||||
if code != "" {
|
||||
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||
log.Debug("Failed to set code state: ", err)
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
db.Provider.DeleteOTP(gc, otp)
|
||||
if isSignUp {
|
||||
|
|
|
@ -51,24 +51,15 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||
assert.Nil(t, emailTemplate)
|
||||
})
|
||||
|
||||
t.Run("should not add email template with empty design", func(t *testing.T) {
|
||||
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
||||
EventName: s.TestInfo.TestEmailTemplateEventTypes[0],
|
||||
Template: "test",
|
||||
Subject: "test",
|
||||
Design: " ",
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, emailTemplate)
|
||||
})
|
||||
design := ""
|
||||
|
||||
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
||||
t.Run("should add email template for "+eventType, func(t *testing.T) {
|
||||
t.Run("should add email template with empty design for "+eventType, func(t *testing.T) {
|
||||
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
||||
EventName: eventType,
|
||||
Template: "Test email",
|
||||
Subject: "Test email",
|
||||
Design: "Test design",
|
||||
Design: &design,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, emailTemplate)
|
||||
|
@ -78,7 +69,7 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, et.EventName, eventType)
|
||||
assert.Equal(t, "Test email", et.Subject)
|
||||
assert.Equal(t, "Test design", et.Design)
|
||||
assert.Equal(t, "", et.Design)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -51,7 +51,8 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
|
|||
gc, err := utils.GinContextFromContext(ctx)
|
||||
assert.NoError(t, err)
|
||||
sessionKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth)
|
||||
nonce := uuid.New().String()
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, "")
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package token
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
@ -10,7 +12,6 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/robertkrimen/otto"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
|
@ -47,30 +48,9 @@ type SessionData struct {
|
|||
LoginMethod string `json:"login_method"`
|
||||
}
|
||||
|
||||
// CreateSessionToken creates a new session token
|
||||
func CreateSessionToken(user models.User, nonce string, roles, scope []string, loginMethod string) (*SessionData, string, error) {
|
||||
fingerPrintMap := &SessionData{
|
||||
Nonce: nonce,
|
||||
Roles: roles,
|
||||
Subject: user.ID,
|
||||
Scope: scope,
|
||||
LoginMethod: loginMethod,
|
||||
IssuedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().AddDate(1, 0, 0).Unix(),
|
||||
}
|
||||
fingerPrintBytes, _ := json.Marshal(fingerPrintMap)
|
||||
fingerPrintHash, err := crypto.EncryptAES(string(fingerPrintBytes))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return fingerPrintMap, fingerPrintHash, nil
|
||||
}
|
||||
|
||||
// CreateAuthToken creates a new auth token when userlogs in
|
||||
func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, loginMethod string) (*Token, error) {
|
||||
func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, loginMethod, nonce string, code string) (*Token, error) {
|
||||
hostname := parsers.GetHost(gc)
|
||||
nonce := uuid.New().String()
|
||||
_, fingerPrintHash, err := CreateSessionToken(user, nonce, roles, scope, loginMethod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -80,7 +60,23 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, l
|
|||
return nil, err
|
||||
}
|
||||
|
||||
idToken, idTokenExpiresAt, err := CreateIDToken(user, roles, hostname, nonce, loginMethod)
|
||||
atHash := sha256.New()
|
||||
atHash.Write([]byte(accessToken))
|
||||
atHashBytes := atHash.Sum(nil)
|
||||
// hashedToken := string(bs)
|
||||
atHashDigest := atHashBytes[0 : len(atHashBytes)/2]
|
||||
atHashString := base64.RawURLEncoding.EncodeToString(atHashDigest)
|
||||
|
||||
codeHashString := ""
|
||||
if code != "" {
|
||||
codeHash := sha256.New()
|
||||
codeHash.Write([]byte(code))
|
||||
codeHashBytes := codeHash.Sum(nil)
|
||||
codeHashDigest := codeHashBytes[0 : len(codeHashBytes)/2]
|
||||
codeHashString = base64.RawURLEncoding.EncodeToString(codeHashDigest)
|
||||
}
|
||||
|
||||
idToken, idTokenExpiresAt, err := CreateIDToken(user, roles, hostname, nonce, atHashString, codeHashString, loginMethod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -104,6 +100,26 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, l
|
|||
return res, nil
|
||||
}
|
||||
|
||||
// CreateSessionToken creates a new session token
|
||||
func CreateSessionToken(user models.User, nonce string, roles, scope []string, loginMethod string) (*SessionData, string, error) {
|
||||
fingerPrintMap := &SessionData{
|
||||
Nonce: nonce,
|
||||
Roles: roles,
|
||||
Subject: user.ID,
|
||||
Scope: scope,
|
||||
LoginMethod: loginMethod,
|
||||
IssuedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().AddDate(1, 0, 0).Unix(),
|
||||
}
|
||||
fingerPrintBytes, _ := json.Marshal(fingerPrintMap)
|
||||
fingerPrintHash, err := crypto.EncryptAES(string(fingerPrintBytes))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return fingerPrintMap, fingerPrintHash, nil
|
||||
}
|
||||
|
||||
// CreateRefreshToken util to create JWT token
|
||||
func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonce, loginMethod string) (string, int64, error) {
|
||||
// expires in 1 year
|
||||
|
@ -124,6 +140,7 @@ func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonc
|
|||
"scope": scopes,
|
||||
"nonce": nonce,
|
||||
"login_method": loginMethod,
|
||||
"allowed_roles": strings.Split(user.Roles, ","),
|
||||
}
|
||||
|
||||
token, err := SignJWTToken(customClaims)
|
||||
|
@ -163,6 +180,7 @@ func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce
|
|||
"scope": scopes,
|
||||
"roles": roles,
|
||||
"login_method": loginMethod,
|
||||
"allowed_roles": strings.Split(user.Roles, ","),
|
||||
}
|
||||
|
||||
token, err := SignJWTToken(customClaims)
|
||||
|
@ -256,7 +274,6 @@ func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]inte
|
|||
if loginMethod != nil && loginMethod != "" {
|
||||
sessionKey = loginMethod.(string) + ":" + userID
|
||||
}
|
||||
|
||||
token, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+nonce)
|
||||
if nonce == "" || err != nil {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
|
@ -317,7 +334,9 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
|
|||
|
||||
// CreateIDToken util to create JWT token, based on
|
||||
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||
func CreateIDToken(user models.User, roles []string, hostname, nonce, loginMethod string) (string, int64, error) {
|
||||
// For response_type (code) / authorization_code grant nonce should be empty
|
||||
// for implicit flow it should be present to verify with actual state
|
||||
func CreateIDToken(user models.User, roles []string, hostname, nonce, atHash, cHash, loginMethod string) (string, int64, error) {
|
||||
expireTime, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAccessTokenExpiryTime)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
|
@ -343,10 +362,10 @@ func CreateIDToken(user models.User, roles []string, hostname, nonce, loginMetho
|
|||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
customClaims := jwt.MapClaims{
|
||||
"iss": hostname,
|
||||
"aud": clientID,
|
||||
"nonce": nonce,
|
||||
"sub": user.ID,
|
||||
"exp": expiresAt,
|
||||
"iat": time.Now().Unix(),
|
||||
|
@ -356,6 +375,16 @@ func CreateIDToken(user models.User, roles []string, hostname, nonce, loginMetho
|
|||
claimKey: roles,
|
||||
}
|
||||
|
||||
// split nonce to see if its authorization code grant method
|
||||
|
||||
if cHash != "" {
|
||||
customClaims["at_hash"] = atHash
|
||||
customClaims["c_hash"] = cHash
|
||||
} else {
|
||||
customClaims["nonce"] = nonce
|
||||
customClaims["at_hash"] = atHash
|
||||
}
|
||||
|
||||
for k, v := range userMap {
|
||||
if k != "roles" {
|
||||
customClaims[k] = v
|
||||
|
|
|
@ -7,8 +7,6 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// TODO re-name GinContextKey -> GinContext
|
||||
|
||||
// GinContext to get gin context from context
|
||||
func GinContextFromContext(ctx context.Context) (*gin.Context, error) {
|
||||
ginContext := ctx.Value("GinContextKey")
|
||||
|
|
13
templates/authorize_form_post.tmpl
Normal file
13
templates/authorize_form_post.tmpl
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Authorization Response</title>
|
||||
</head>
|
||||
<body onload="document.forms['authorize_form_post'].submit()">
|
||||
<form action="{{.target_origin}}" name="authorize_form_post" method="POST">
|
||||
{{ range $key, $val := .authorization_response }}
|
||||
<input type="hidden" key="{{$key}}" value="{{$val}}" name="{{$key}}" id="{{$key}}" />
|
||||
{{ end }}
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user