Compare commits

...

36 Commits

Author SHA1 Message Date
Lakhan Samani
fedc3173fe fix: get nonce from query request if possible 2022-10-19 23:36:33 +05:30
Lakhan Samani
de4381261e fix: add nonce to supported claims 2022-10-19 23:17:13 +05:30
Lakhan Samani
a916b8c32c fix: add nonce 2022-10-19 19:04:15 +05:30
Lakhan Samani
89f08b6d31 fix: redirect from app 2022-10-19 12:20:22 +05:30
Lakhan Samani
cc23784df8 fix: add code to login query params 2022-10-19 12:01:34 +05:30
Lakhan Samani
7ff3b3018a fix: add code to query params 2022-10-19 11:29:49 +05:30
Lakhan Samani
2b52932e98 fix: add code to other response methods 2022-10-19 09:03:00 +05:30
Lakhan Samani
c716638725 fix(server): revert the state & code_challenge validation 2022-10-18 23:24:19 +05:30
Lakhan Samani
252cd1fa2d fix: make code_challenge optional 2022-10-18 23:14:24 +05:30
Lakhan Samani
7c2693b086 fix: form post template 2022-10-18 23:03:55 +05:30
Lakhan Samani
eaa10ec5bc fix: error detection 2022-10-18 22:34:57 +05:30
Lakhan Samani
253128ca0c fix: query params for code response 2022-10-18 22:00:54 +05:30
Lakhan Samani
cddfe1e088 fix: response 2022-10-18 21:46:37 +05:30
Lakhan Samani
8e655bcb5b fix: authorize response 2022-10-18 21:29:09 +05:30
Lakhan Samani
9a411e673c fix: reponse 2022-10-18 21:08:53 +05:30
Lakhan Samani
346c8e5a47 fix: handle response 2022-10-16 22:16:37 +05:30
Lakhan Samani
3cd99fe5f6 fix: open id config 2022-10-16 21:03:37 +05:30
Lakhan Samani
2bd92d6028 feat: add form_post method 2022-10-16 20:46:54 +05:30
Lakhan Samani
ff805e3ef2 fix: add comments 2022-10-12 13:10:24 +05:30
Lakhan Samani
0115128ee7 fix(server): authorizer as oauth provider 2022-10-09 19:48:13 +05:30
Lakhan Samani
d8eceadd7f Merge pull request #267 from authorizerdev/fix/sqlserver-text-type
fix(server): text type for sql server 2019
2022-10-08 15:38:32 +05:30
Lakhan Samani
e6c4fdff26 fix(server): text type for sql server 2019
Resolves #266
2022-10-07 10:13:20 +05:30
Lakhan Samani
e760a5598e Merge pull request #260 from authorizerdev/chore/add-prettier-app
chore(app): add prettier
2022-10-02 22:40:16 +05:30
Lakhan Samani
f62a22619b chore(app): add prettier 2022-10-02 22:39:47 +05:30
Lakhan Samani
c32a7fa1e4 Merge pull request #259 from authorizerdev/chore/add-prettier-dashboard
chore(dashboard): add prettier
2022-10-02 22:38:06 +05:30
Lakhan Samani
399b97079d chore(dashboard): add prettier 2022-10-02 22:37:20 +05:30
Lakhan Samani
fe687cb0ca Merge pull request #258 from authorizerdev/fix/cookie-security-features
feat(dashboard): allow setting admin / app cookie security
2022-10-02 22:03:43 +05:30
Lakhan Samani
9cb011e921 feat(dashboard): allow setting admin / app cookie security
Fixes #233
2022-10-02 22:01:22 +05:30
Lakhan Samani
4e1bba2ba8 Merge pull request #246 from jerebtw/main
feat: add app & admin cookie secure variable to dashboard
2022-10-02 21:36:36 +05:30
ruessej
f1509f90f0 feat: Update generated and models_gen 2022-10-01 18:12:49 +02:00
Jerebtw
bd4d48c7c5 fix: schema.graphqls 2022-10-01 17:57:23 +02:00
Jerebtw
0e3242372b feat: add app & admin cookie secure variable to dashboard
Todo: Generate graphql (i don't work on my PC (Windows))
2022-10-01 17:48:05 +02:00
Lakhan Samani
89cea39c41 Merge pull request #257 from authorizerdev/chore/update-go-gin-1.18.1
chore: update go-gin server to 1.18.1
2022-10-01 17:42:00 +05:30
Lakhan Samani
570a0b9531 chore: update go-gin server to 1.18.1 2022-10-01 17:41:16 +05:30
Lakhan Samani
686b3a4666 Merge pull request #255 from authorizerdev/chore/update-go-1.19.1
chore: update golang to 1.19.1
2022-10-01 17:20:48 +05:30
Lakhan Samani
b266a14108 chore: update golang to 1.19.1 2022-10-01 15:17:11 +05:30
58 changed files with 2195 additions and 814 deletions

View File

@@ -1,4 +1,4 @@
FROM golang:1.17-alpine as go-builder
FROM golang:1.19.1-alpine as go-builder
WORKDIR /authorizer
COPY server server
COPY Makefile .

6
app/.prettierrc.json Normal file
View File

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

24
app/package-lock.json generated
View File

@@ -22,7 +22,8 @@
},
"devDependencies": {
"@types/react-router-dom": "^5.1.8",
"@types/styled-components": "^5.1.11"
"@types/styled-components": "^5.1.11",
"prettier": "2.7.1"
}
},
"node_modules/@authorizerdev/authorizer-js": {
@@ -617,6 +618,21 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"node_modules/prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
@@ -1329,6 +1345,12 @@
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
},
"prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",

View File

@@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
"start": "NODE_ENV=development node ./esbuild.config.js"
"start": "NODE_ENV=development node ./esbuild.config.js",
"format": "prettier --write --use-tabs 'src/**/*.(ts|tsx|js|jsx)'"
},
"keywords": [],
"author": "Lakhan Samani",
@@ -19,11 +20,12 @@
"react-dom": "^17.0.2",
"react-is": "^17.0.2",
"react-router-dom": "^5.2.0",
"typescript": "^4.3.5",
"styled-components": "^5.3.0"
"styled-components": "^5.3.0",
"typescript": "^4.3.5"
},
"devDependencies": {
"@types/react-router-dom": "^5.1.8",
"@types/styled-components": "^5.1.11"
"@types/styled-components": "^5.1.11",
"prettier": "2.7.1"
}
}

View File

@@ -5,9 +5,9 @@ import Root from './Root';
import { createRandomString } from './utils/common';
declare global {
interface Window {
__authorizer__: any;
}
interface Window {
__authorizer__: any;
}
}
export default function App() {

View File

@@ -32,12 +32,13 @@ export default function Root({
const { token, loading, config } = useAuthorizer();
const searchParams = new URLSearchParams(
hasWindow() ? window.location.search : ``
hasWindow() ? window.location.search : ``,
);
const state = searchParams.get('state') || createRandomString();
const scope = searchParams.get('scope')
? searchParams.get('scope')?.toString().split(' ')
: ['openid', 'profile', 'email'];
const code = searchParams.get('code') || ''
const urlProps: Record<string, any> = {
state,
@@ -58,9 +59,15 @@ 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 (token.refresh_token) {
params += `&refresh_token=${token.refresh_token}`;
}
const url = new URL(redirectURL);
if (redirectURL.includes('?')) {
redirectURL = `${redirectURL}&${params}`;

View File

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

View File

@@ -8,7 +8,7 @@ export const createRandomString = () => {
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
let random = '';
const randomValues = Array.from(
getCrypto().getRandomValues(new Uint8Array(43))
getCrypto().getRandomValues(new Uint8Array(43)),
);
randomValues.forEach((v) => (random += charset[v % charset.length]));
return random;

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
"start": "NODE_ENV=development node ./esbuild.config.js"
"start": "NODE_ENV=development node ./esbuild.config.js",
"format": "prettier --write --use-tabs 'src/**/*.(ts|tsx|js|jsx)'"
},
"keywords": [],
"author": "Lakhan Samani",
@@ -35,6 +36,7 @@
"urql": "^2.0.6"
},
"devDependencies": {
"@types/react-email-editor": "^1.1.7"
"@types/react-email-editor": "^1.1.7",
"prettier": "2.7.1"
}
}

View File

@@ -82,7 +82,7 @@ const EditUserModal = ({
// @ts-ignore
[property]: userData[property],
}),
{}
{},
);
const res = await client
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })

View File

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

View File

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

View File

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

View File

@@ -126,6 +126,40 @@ const Features = ({ variables, setVariables }: any) => {
</Flex>
</Flex>
</Stack>
<Divider paddingY={5} />
<Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
Cookie Security Features
</Text>
<Stack spacing={6}>
<Flex>
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Use Secure App Cookie:</Text>
<Text fontSize="x-small">
Note: If you set this to insecure, it will set{' '}
<code>sameSite</code> property of cookie to <code>lax</code> mode
</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.APP_COOKIE_SECURE}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Use Secure Admin Cookie:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.ADMIN_COOKIE_SECURE}
/>
</Flex>
</Flex>
</Stack>
</div>
);
};

View File

@@ -37,7 +37,7 @@ const JSTConfigurations = ({
JSON.stringify({
type: variables.JWT_TYPE,
key: variables.JWT_PUBLIC_KEY || variables.JWT_SECRET,
})
}),
);
toast({
title: `JWT config copied successfully`,

View File

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

View File

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

View File

@@ -167,7 +167,7 @@ const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
) : (
<>
{Object.values(HMACEncryptionType).includes(
stateVariables.JWT_TYPE
stateVariables.JWT_TYPE,
) ? (
<Flex marginTop="8">
<Flex w="23%" justifyContent="start" alignItems="center">

View File

@@ -64,7 +64,7 @@ const InputField = ({
const updateInputHandler = (
type: string,
operation: any,
role: string = ''
role: string = '',
) => {
if (operation === ArrayInputOperations.APPEND) {
if (inputData[type] !== '') {
@@ -78,7 +78,7 @@ const InputField = ({
}
if (operation === ArrayInputOperations.REMOVE) {
let updatedEnvVars = variables[type].filter(
(item: string) => item !== role
(item: string) => item !== role,
);
setVariables({
...variables,
@@ -95,7 +95,7 @@ const InputField = ({
onChange={(
event: Event & {
target: HTMLInputElement;
}
},
) =>
setVariables({
...variables,
@@ -120,7 +120,7 @@ const InputField = ({
onChange={(
event: Event & {
target: HTMLInputElement;
}
},
) =>
setVariables({
...variables,
@@ -207,7 +207,7 @@ const InputField = ({
updateInputHandler(
inputType,
ArrayInputOperations.REMOVE,
role
role,
)
}
/>
@@ -288,7 +288,7 @@ const InputField = ({
onChange={(
event: Event & {
target: HTMLInputElement;
}
},
) =>
setVariables({
...variables,

View File

@@ -304,7 +304,7 @@ const InviteMembersModal = ({
onClick={() =>
updateEmailListHandler(
ArrayInputOperations.REMOVE,
index
index,
)
}
>

View File

@@ -218,7 +218,7 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
</NavItem>{' '}
</Text>
</NavLink>
)
),
)}
<Link
href="/playground"

View File

@@ -185,7 +185,7 @@ const UpdateEmailTemplate = ({
toast({
title: capitalizeFirstLetter(
res.data?._add_email_template?.message ||
res.data?._update_email_template?.message
res.data?._update_email_template?.message,
),
isClosable: true,
status: 'success',
@@ -220,7 +220,7 @@ const UpdateEmailTemplate = ({
}, [isOpen]);
useEffect(() => {
const updatedTemplateVariables = Object.entries(
emailTemplateVariables
emailTemplateVariables,
).reduce((acc, [key, val]): any => {
if (
(templateData[EmailTemplateInputDataFields.EVENT_NAME] !==
@@ -367,7 +367,7 @@ const UpdateEmailTemplate = ({
onChange={(e) =>
inputChangehandler(
EmailTemplateInputDataFields.EVENT_NAME,
e.currentTarget.value
e.currentTarget.value,
)
}
>
@@ -376,7 +376,7 @@ const UpdateEmailTemplate = ({
<option value={value} key={key}>
{key}
</option>
)
),
)}
</Select>
</Flex>
@@ -401,7 +401,7 @@ const UpdateEmailTemplate = ({
onChange={(e) =>
inputChangehandler(
EmailTemplateInputDataFields.SUBJECT,
e.currentTarget.value
e.currentTarget.value,
)
}
/>

View File

@@ -126,13 +126,13 @@ const UpdateWebhookModal = ({
...initWebhookValidatorData,
});
const [verifiedStatus, setVerifiedStatus] = useState<webhookVerifiedStatus>(
webhookVerifiedStatus.PENDING
webhookVerifiedStatus.PENDING,
);
const inputChangehandler = (
inputType: string,
value: any,
headerInputType: string = WebhookInputHeaderFields.KEY,
headerIndex: number = 0
headerIndex: number = 0,
) => {
if (
verifiedStatus !== webhookVerifiedStatus.PENDING &&
@@ -238,7 +238,7 @@ const UpdateWebhookModal = ({
validator[WebhookInputDataFields.ENDPOINT] &&
!validator[WebhookInputDataFields.HEADERS].some(
(headerData: headersValidatorDataType) =>
!headerData.key || !headerData.value
!headerData.key || !headerData.value,
)
);
};
@@ -256,7 +256,7 @@ const UpdateWebhookModal = ({
(acc, data) => {
return data.key ? { ...acc, [data.key]: data.value } : acc;
},
{}
{},
);
if (Object.keys(headers).length) {
params[WebhookInputDataFields.HEADERS] = headers;
@@ -295,7 +295,7 @@ const UpdateWebhookModal = ({
} else if (res.data?._add_webhook || res.data?._update_webhook) {
toast({
title: capitalizeFirstLetter(
res.data?._add_webhook?.message || res.data?._update_webhook?.message
res.data?._add_webhook?.message || res.data?._update_webhook?.message,
),
isClosable: true,
status: 'success',
@@ -333,7 +333,7 @@ const UpdateWebhookModal = ({
setValidator({
...validator,
[WebhookInputDataFields.HEADERS]: new Array(
formattedHeadersData.length
formattedHeadersData.length,
)
.fill({})
.map(() => ({ ...initHeadersValidatorData })),
@@ -406,7 +406,7 @@ const UpdateWebhookModal = ({
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.EVENT_NAME,
e.currentTarget.value
e.currentTarget.value,
)
}
>
@@ -415,7 +415,7 @@ const UpdateWebhookModal = ({
<option value={value} key={key}>
{key}
</option>
)
),
)}
</Select>
</Flex>
@@ -438,7 +438,7 @@ const UpdateWebhookModal = ({
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.ENDPOINT,
e.currentTarget.value
e.currentTarget.value,
)
}
/>
@@ -462,7 +462,7 @@ const UpdateWebhookModal = ({
onChange={() =>
inputChangehandler(
WebhookInputDataFields.ENABLED,
!webhook[WebhookInputDataFields.ENABLED]
!webhook[WebhookInputDataFields.ENABLED],
)
}
/>
@@ -517,7 +517,7 @@ const UpdateWebhookModal = ({
WebhookInputDataFields.HEADERS,
e.target.value,
WebhookInputHeaderFields.KEY,
index
index,
)
}
width="30%"
@@ -540,7 +540,7 @@ const UpdateWebhookModal = ({
WebhookInputDataFields.HEADERS,
e.target.value,
WebhookInputHeaderFields.VALUE,
index
index,
)
}
width="65%"
@@ -560,7 +560,7 @@ const UpdateWebhookModal = ({
</InputRightElement>
</InputGroup>
</Flex>
)
),
)}
</Flex>
<Divider marginY={5} />

View File

@@ -161,15 +161,15 @@ const ViewWebhookLogsModal = ({
<Td>
<Text fontSize="sm">{`${logData.id.substring(
0,
5
5,
)}***${logData.id.substring(
logData.id.length - 5,
logData.id.length
logData.id.length,
)}`}</Text>
</Td>
<Td>
{dayjs(logData.created_at * 1000).format(
'MMM DD, YYYY'
'MMM DD, YYYY',
)}
</Td>
<Td>

View File

@@ -63,6 +63,8 @@ export const TextAreaInputType = {
};
export const SwitchInputType = {
APP_COOKIE_SECURE: 'APP_COOKIE_SECURE',
ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE',
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
@@ -133,6 +135,8 @@ export interface envVarTypes {
ORGANIZATION_LOGO: string;
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
ADMIN_SECRET: string;
APP_COOKIE_SECURE: boolean;
ADMIN_COOKIE_SECURE: boolean;
DISABLE_LOGIN_PAGE: boolean;
DISABLE_MAGIC_LINK_LOGIN: boolean;
DISABLE_EMAIL_VERIFICATION: boolean;

View File

@@ -50,6 +50,8 @@ export const EnvVariablesQuery = `
ORGANIZATION_NAME
ORGANIZATION_LOGO
ADMIN_SECRET
APP_COOKIE_SECURE
ADMIN_COOKIE_SECURE
DISABLE_LOGIN_PAGE
DISABLE_MAGIC_LINK_LOGIN
DISABLE_EMAIL_VERIFICATION

View File

@@ -6,5 +6,5 @@ ReactDOM.render(
<div>
<App />
</div>,
document.getElementById('root')
document.getElementById('root'),
);

View File

@@ -154,7 +154,7 @@ const EmailTemplates = () => {
<Td>{templateData[EmailTemplateInputDataFields.SUBJECT]}</Td>
<Td>
{dayjs(templateData.created_at * 1000).format(
'MMM DD, YYYY'
'MMM DD, YYYY',
)}
</Td>
<Td>

View File

@@ -71,6 +71,8 @@ const Environment = () => {
ORGANIZATION_LOGO: '',
CUSTOM_ACCESS_TOKEN_SCRIPT: '',
ADMIN_SECRET: '',
APP_COOKIE_SECURE: false,
ADMIN_COOKIE_SECURE: false,
DISABLE_LOGIN_PAGE: false,
DISABLE_MAGIC_LINK_LOGIN: false,
DISABLE_EMAIL_VERIFICATION: false,
@@ -155,7 +157,7 @@ const Environment = () => {
// @ts-ignore
[property]: envVariables[property],
}),
{}
{},
);
if (
updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' ||

View File

@@ -29,7 +29,7 @@ import {
MenuItem,
useToast,
Spinner,
TableContainer
TableContainer,
} from '@chakra-ui/react';
import {
FaAngleLeft,
@@ -195,7 +195,7 @@ export default function Users() {
const updateAccessHandler = async (
id: string,
action: updateAccessActions
action: updateAccessActions,
) => {
switch (action) {
case updateAccessActions.ENABLE:
@@ -263,8 +263,9 @@ export default function Users() {
.toPromise();
if (res.data?._update_user?.id) {
toast({
title: `Multi factor authentication ${user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
} for user`,
title: `Multi factor authentication ${
user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
} for user`,
isClosable: true,
status: 'success',
position: 'bottom-right',
@@ -387,7 +388,7 @@ export default function Users() {
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.ENABLE
updateAccessActions.ENABLE,
)
}
>
@@ -398,7 +399,7 @@ export default function Users() {
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.REVOKE
updateAccessActions.REVOKE,
)
}
>
@@ -407,13 +408,17 @@ export default function Users() {
)}
{user.is_multi_factor_auth_enabled ? (
<MenuItem
onClick={() => multiFactorAuthUpdateHandler(user)}
onClick={() =>
multiFactorAuthUpdateHandler(user)
}
>
Disable MultiFactor Authentication
</MenuItem>
) : (
<MenuItem
onClick={() => multiFactorAuthUpdateHandler(user)}
onClick={() =>
multiFactorAuthUpdateHandler(user)
}
>
Enable MultiFactor Authentication
</MenuItem>

View File

@@ -170,12 +170,12 @@ const Webhooks = () => {
label={JSON.stringify(
webhook[WebhookInputDataFields.HEADERS],
null,
' '
' ',
)}
>
<Tag size="sm" variant="outline" colorScheme="gray">
{Object.keys(
webhook[WebhookInputDataFields.HEADERS] || {}
webhook[WebhookInputDataFields.HEADERS] || {},
)?.length.toString()}
</Tag>
</Tooltip>

View File

@@ -67,7 +67,7 @@ export const validateEmail = (email: string) => {
return email
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
)
? true
: false;
@@ -78,7 +78,7 @@ export const validateURI = (uri: string) => {
return uri
.toLowerCase()
.match(
/(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/
/(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/,
)
? true
: false;

View File

@@ -27,7 +27,7 @@ const parseCSV = (file: File, delimiter: string): Promise<dataTypes[]> => {
value: email.trim(),
isInvalid: !validateEmail(email.trim()),
};
})
}),
);
};

View 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"
)

View File

@@ -29,8 +29,7 @@ func SetSession(gc *gin.Context, sessionID string) {
domain = "." + domain
}
// Use sameSite = lax by default
// Since app cookie can come from cross site it becomes important to set this in lax mode.
// Since app cookie can come from cross site it becomes important to set this in lax mode when insecure.
// Example person using custom UI on their app domain and making request to authorizer domain.
// For more information check:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite

View File

@@ -12,9 +12,9 @@ type EmailTemplate struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
Subject string `gorm:"type:text" json:"subject" bson:"subject" cql:"subject"`
Template string `gorm:"type:text" json:"template" bson:"template" cql:"template"`
Design string `gorm:"type:text" json:"design" bson:"design" cql:"design"`
Subject string `json:"subject" bson:"subject" cql:"subject"`
Template string `json:"template" bson:"template" cql:"template"`
Design string `json:"design" bson:"design" cql:"design"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
}

View File

@@ -6,8 +6,8 @@ package models
type Env struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
EnvData string `gorm:"type:text" json:"env" bson:"env" cql:"env"`
Hash string `gorm:"type:text" json:"hash" bson:"hash" cql:"hash"`
EnvData string `json:"env" bson:"env" cql:"env"`
Hash string `json:"hash" bson:"hash" cql:"hash"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
}

View File

@@ -17,7 +17,7 @@ type User struct {
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
Password *string `json:"password" bson:"password" cql:"password"`
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
@@ -27,7 +27,7 @@ type User struct {
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
Picture *string `json:"picture" bson:"picture" cql:"picture"`
Roles string `json:"roles" bson:"roles" cql:"roles"`
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled"`

View File

@@ -13,12 +13,12 @@ import (
type VerificationRequest struct {
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
Token string `gorm:"type:text" json:"token" bson:"token" cql:"jwt_token"` // token is reserved keyword in cassandra
Token string `json:"token" bson:"token" cql:"jwt_token"` // token is reserved keyword in cassandra
Identifier string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(64)" json:"identifier" bson:"identifier" cql:"identifier"`
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at"`
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email"`
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce" cql:"nonce"`
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri"`
Nonce string `json:"nonce" bson:"nonce" cql:"nonce"`
RedirectURI string `json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
}

View File

@@ -15,8 +15,8 @@ type Webhook struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name"`
EndPoint string `gorm:"type:text" json:"endpoint" bson:"endpoint" cql:"endpoint"`
Headers string `gorm:"type:text" json:"headers" bson:"headers" cql:"headers"`
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint"`
Headers string `json:"headers" bson:"headers" cql:"headers"`
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`

View File

@@ -14,8 +14,8 @@ type WebhookLog struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status"`
Response string `gorm:"type:text" json:"response" bson:"response" cql:"response"`
Request string `gorm:"type:text" json:"request" bson:"request" cql:"request"`
Response string `json:"response" bson:"response" cql:"response"`
Request string `json:"request" bson:"request" cql:"request"`
WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`

View File

@@ -6,26 +6,28 @@ require (
github.com/99designs/gqlgen v0.17.20
github.com/arangodb/go-driver v1.2.1
github.com/coreos/go-oidc/v3 v3.1.0
github.com/gin-gonic/gin v1.7.2
github.com/go-playground/validator/v10 v10.8.0 // indirect
github.com/gin-gonic/gin v1.8.1
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
github.com/gocql/gocql v1.2.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0
github.com/joho/godotenv v1.3.0
github.com/json-iterator/go v1.1.11 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/robertkrimen/otto v0.0.0-20211024170158-b87d35c0b86f
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.1
github.com/ugorji/go v1.2.6 // indirect
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-20210921155107-089bfa567519
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b // indirect
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

View File

@@ -71,6 +71,7 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
@@ -90,8 +91,8 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
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.7.2 h1:Tg03T9yM2xa8j6I3Z3oqLaQRSmKvxPd6g/2HJ6zICFA=
github.com/gin-gonic/gin v1.7.2/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
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/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=
@@ -99,19 +100,22 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.8.0 h1:1kAa0fCrnpv+QYdkdcRzrRM7AyYs5o8+jZdJCz9xj6k=
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis/v8 v8.11.0 h1:O1Td0mQ8UFChQ3N9zFQqo6kTU2cJ+/it88gDB+zg0wo=
github.com/go-redis/redis/v8 v8.11.0/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE=
github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
@@ -245,9 +249,8 @@ github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
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/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM=
@@ -256,13 +259,15 @@ github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQ
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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=
@@ -278,6 +283,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
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=
@@ -287,9 +293,8 @@ github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -301,6 +306,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
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/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=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -310,6 +319,9 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
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=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
@@ -329,22 +341,22 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4=
github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY=
github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4=
@@ -396,8 +408,10 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
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/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=
@@ -465,8 +479,10 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
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=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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/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=
@@ -528,11 +544,14 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
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-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-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/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=
@@ -684,14 +703,16 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
@@ -714,6 +735,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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=

View File

@@ -71,8 +71,10 @@ type ComplexityRoot struct {
Env struct {
AccessTokenExpiryTime func(childComplexity int) int
AdminCookieSecure func(childComplexity int) int
AdminSecret func(childComplexity int) int
AllowedOrigins func(childComplexity int) int
AppCookieSecure func(childComplexity int) int
AppURL func(childComplexity int) int
AppleClientID func(childComplexity int) int
AppleClientSecret func(childComplexity int) int
@@ -474,6 +476,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.AccessTokenExpiryTime(childComplexity), true
case "Env.ADMIN_COOKIE_SECURE":
if e.complexity.Env.AdminCookieSecure == nil {
break
}
return e.complexity.Env.AdminCookieSecure(childComplexity), true
case "Env.ADMIN_SECRET":
if e.complexity.Env.AdminSecret == nil {
break
@@ -488,6 +497,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Env.AllowedOrigins(childComplexity), true
case "Env.APP_COOKIE_SECURE":
if e.complexity.Env.AppCookieSecure == nil {
break
}
return e.complexity.Env.AppCookieSecure(childComplexity), true
case "Env.APP_URL":
if e.complexity.Env.AppURL == nil {
break
@@ -2079,6 +2095,8 @@ type Env {
TWITTER_CLIENT_SECRET: String
ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String
APP_COOKIE_SECURE: Boolean!
ADMIN_COOKIE_SECURE: Boolean!
}
type ValidateJWTTokenResponse {
@@ -2158,6 +2176,8 @@ input UpdateEnvInput {
ALLOWED_ORIGINS: [String!]
APP_URL: String
RESET_PASSWORD_URL: String
APP_COOKIE_SECURE: Boolean
ADMIN_COOKIE_SECURE: Boolean
DISABLE_EMAIL_VERIFICATION: Boolean
DISABLE_BASIC_AUTHENTICATION: Boolean
DISABLE_MAGIC_LINK_LOGIN: Boolean
@@ -5925,6 +5945,94 @@ func (ec *executionContext) fieldContext_Env_ORGANIZATION_LOGO(ctx context.Conte
return fc, nil
}
func (ec *executionContext) _Env_APP_COOKIE_SECURE(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_APP_COOKIE_SECURE(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.AppCookieSecure, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Env_APP_COOKIE_SECURE(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Env",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Env_ADMIN_COOKIE_SECURE(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Env_ADMIN_COOKIE_SECURE(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.AdminCookieSecure, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Env_ADMIN_COOKIE_SECURE(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Env",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Error_message(ctx context.Context, field graphql.CollectedField, obj *model.Error) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Error_message(ctx, field)
if err != nil {
@@ -9308,6 +9416,10 @@ func (ec *executionContext) fieldContext_Query__env(ctx context.Context, field g
return ec.fieldContext_Env_ORGANIZATION_NAME(ctx, field)
case "ORGANIZATION_LOGO":
return ec.fieldContext_Env_ORGANIZATION_LOGO(ctx, field)
case "APP_COOKIE_SECURE":
return ec.fieldContext_Env_APP_COOKIE_SECURE(ctx, field)
case "ADMIN_COOKIE_SECURE":
return ec.fieldContext_Env_ADMIN_COOKIE_SECURE(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type Env", field.Name)
},
@@ -14858,7 +14970,7 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
asMap[k] = v
}
fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SENDER_EMAIL", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "ORGANIZATION_NAME", "ORGANIZATION_LOGO"}
fieldsInOrder := [...]string{"ACCESS_TOKEN_EXPIRY_TIME", "ADMIN_SECRET", "CUSTOM_ACCESS_TOKEN_SCRIPT", "OLD_ADMIN_SECRET", "SMTP_HOST", "SMTP_PORT", "SMTP_USERNAME", "SMTP_PASSWORD", "SENDER_EMAIL", "JWT_TYPE", "JWT_SECRET", "JWT_PRIVATE_KEY", "JWT_PUBLIC_KEY", "ALLOWED_ORIGINS", "APP_URL", "RESET_PASSWORD_URL", "APP_COOKIE_SECURE", "ADMIN_COOKIE_SECURE", "DISABLE_EMAIL_VERIFICATION", "DISABLE_BASIC_AUTHENTICATION", "DISABLE_MAGIC_LINK_LOGIN", "DISABLE_LOGIN_PAGE", "DISABLE_SIGN_UP", "DISABLE_REDIS_FOR_ENV", "DISABLE_STRONG_PASSWORD", "DISABLE_MULTI_FACTOR_AUTHENTICATION", "ENFORCE_MULTI_FACTOR_AUTHENTICATION", "ROLES", "PROTECTED_ROLES", "DEFAULT_ROLES", "JWT_ROLE_CLAIM", "GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET", "GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET", "FACEBOOK_CLIENT_ID", "FACEBOOK_CLIENT_SECRET", "LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET", "APPLE_CLIENT_ID", "APPLE_CLIENT_SECRET", "TWITTER_CLIENT_ID", "TWITTER_CLIENT_SECRET", "ORGANIZATION_NAME", "ORGANIZATION_LOGO"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -14993,6 +15105,22 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob
if err != nil {
return it, err
}
case "APP_COOKIE_SECURE":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("APP_COOKIE_SECURE"))
it.AppCookieSecure, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
case "ADMIN_COOKIE_SECURE":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("ADMIN_COOKIE_SECURE"))
it.AdminCookieSecure, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
case "DISABLE_EMAIL_VERIFICATION":
var err error
@@ -16069,6 +16197,20 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._Env_ORGANIZATION_LOGO(ctx, field, obj)
case "APP_COOKIE_SECURE":
out.Values[i] = ec._Env_APP_COOKIE_SECURE(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "ADMIN_COOKIE_SECURE":
out.Values[i] = ec._Env_ADMIN_COOKIE_SECURE(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}

View File

@@ -110,6 +110,8 @@ type Env struct {
TwitterClientSecret *string `json:"TWITTER_CLIENT_SECRET"`
OrganizationName *string `json:"ORGANIZATION_NAME"`
OrganizationLogo *string `json:"ORGANIZATION_LOGO"`
AppCookieSecure bool `json:"APP_COOKIE_SECURE"`
AdminCookieSecure bool `json:"ADMIN_COOKIE_SECURE"`
}
type Error struct {
@@ -277,6 +279,8 @@ type UpdateEnvInput struct {
AllowedOrigins []string `json:"ALLOWED_ORIGINS"`
AppURL *string `json:"APP_URL"`
ResetPasswordURL *string `json:"RESET_PASSWORD_URL"`
AppCookieSecure *bool `json:"APP_COOKIE_SECURE"`
AdminCookieSecure *bool `json:"ADMIN_COOKIE_SECURE"`
DisableEmailVerification *bool `json:"DISABLE_EMAIL_VERIFICATION"`
DisableBasicAuthentication *bool `json:"DISABLE_BASIC_AUTHENTICATION"`
DisableMagicLinkLogin *bool `json:"DISABLE_MAGIC_LINK_LOGIN"`

View File

@@ -146,6 +146,8 @@ type Env {
TWITTER_CLIENT_SECRET: String
ORGANIZATION_NAME: String
ORGANIZATION_LOGO: String
APP_COOKIE_SECURE: Boolean!
ADMIN_COOKIE_SECURE: Boolean!
}
type ValidateJWTTokenResponse {
@@ -225,6 +227,8 @@ input UpdateEnvInput {
ALLOWED_ORIGINS: [String!]
APP_URL: String
RESET_PASSWORD_URL: String
APP_COOKIE_SECURE: Boolean
ADMIN_COOKIE_SECURE: Boolean
DISABLE_EMAIL_VERIFICATION: Boolean
DISABLE_BASIC_AUTHENTICATION: Boolean
DISABLE_MAGIC_LINK_LOGIN: Boolean

View File

@@ -1,6 +1,7 @@
package handlers
import (
"fmt"
"net/http"
"strconv"
"strings"
@@ -26,6 +27,12 @@ import (
// 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.
const (
authorizeWebMessageTemplate = "authorize_web_message.tmpl"
authorizeFormPostTemplate = "authorize_form_post.tmpl"
)
func AuthorizeHandler() gin.HandlerFunc {
return func(gc *gin.Context) {
redirectURI := strings.TrimSpace(gc.Query("redirect_uri"))
@@ -34,8 +41,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 +52,100 @@ 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",
},
},
})
}
return
}
log := log.WithFields(log.Fields{
"response_mode": responseMode,
"response_type": responseType,
"state": state,
"code_challenge": codeChallenge,
"scope": scope,
"redirect_uri": redirectURI,
})
code := uuid.New().String()
if nonce == "" {
nonce = uuid.New().String()
}
memorystore.Provider.SetState(codeChallenge, code)
// used for response mode query or fragment
loginState := "state=" + state + "&scope=" + strings.Join(scope, " ") + "&redirect_uri=" + redirectURI + "&code=" + code
loginURL := "/app?" + loginState
if responseMode == constants.ResponseModeFragment {
loginURL = "/app#" + loginState
}
if state == "" {
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
"type": "authorization_response",
"response": map[string]interface{}{
"error": "state_required",
"error_description": "state is required",
},
}, http.StatusOK)
return
}
if responseType == constants.ResponseTypeCode && codeChallenge == "" {
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
"type": "authorization_response",
"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 {
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("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{}{
"type": "authorization_response",
"response": map[string]string{
"error": "signup_required",
"error_description": "Sign up required",
},
},
})
}
log.Debug("GetUserByID failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, map[string]interface{}{
"type": "authorization_response",
"response": map[string]interface{}{
"error": "signup_required",
"error_description": "Sign up required",
},
}, http.StatusOK)
return
}
@@ -223,72 +154,90 @@ 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()
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",
},
},
})
}
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{}{
"type": "authorization_response",
"response": map[string]string{
"code": code,
"state": state,
},
},
})
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
if err != nil {
log.Debug("CreateSessionToken failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return
}
if isResponseTypeToken {
// rollover the session for security
authToken, err := token.CreateAuthToken(gc, user, 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",
},
},
})
}
if err := memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken); err != nil {
log.Debug("SetState failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return
}
// rollover the session for security
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
if responseType == constants.ResponseTypeCode {
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
}
cookie.SetSession(gc, newSessionToken)
// 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
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]interface{}{
"code": code,
"state": state,
},
}, http.StatusOK)
return
}
if responseType == constants.ResponseTypeToken || responseType == constants.ResponseTypeIDToken {
// rollover the session for security
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod)
if err != nil {
log.Debug("CreateAuthToken failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return
}
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, 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+"_"+authToken.FingerPrint, authToken.AccessToken.Token); 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()
@@ -297,7 +246,7 @@ func AuthorizeHandler() gin.HandlerFunc {
}
// 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(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token + "&code=" + code
res := map[string]interface{}{
"access_token": authToken.AccessToken.Token,
@@ -306,6 +255,7 @@ func AuthorizeHandler() gin.HandlerFunc {
"scope": scope,
"token_type": "Bearer",
"expires_in": expiresIn,
"code": code,
}
if authToken.RefreshToken != nil {
@@ -314,38 +264,72 @@ 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 {
redirectURI = redirectURI + "#" + params
}
} else {
gc.HTML(http.StatusOK, template, gin.H{
"target_origin": redirectURI,
"authorization_response": map[string]interface{}{
"type": "authorization_response",
"response": res,
},
})
}
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{
"target_origin": redirectURI,
"authorization_response": map[string]interface{}{
"type": "authorization_response",
"response": map[string]string{
"error": "login_required",
"error_description": "Login is required",
},
},
})
}
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
}
}
func validateAuthorizeRequest(responseType, responseMode, clientID, state, codeChallenge string) error {
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
}
switch responseMode {
case constants.ResponseModeQuery, constants.ResponseModeFragment:
if isAuthenticationRequired {
gc.Redirect(http.StatusFound, loginURI)
} else {
gc.Redirect(http.StatusFound, redirectURI)
}
return
case constants.ResponseModeWebMessage:
gc.HTML(httpStatusCode, authorizeWebMessageTemplate, gin.H{
"target_origin": redirectURI,
"authorization_response": data,
})
return
case constants.ResponseModeFormPost:
gc.HTML(httpStatusCode, authorizeFormPostTemplate, gin.H{
"target_origin": redirectURI,
"authorization_response": data["response"],
})
return
}
}

View File

@@ -20,11 +20,11 @@ func OpenIDConfigurationHandler() gin.HandlerFunc {
"token_endpoint": issuer + "/token",
"userinfo_endpoint": issuer + "/userinfo",
"jwks_uri": issuer + "/.well-known/jwks.json",
"response_types_supported": []string{"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"},
"response_types_supported": []string{"code", "token", "id_token"},
"scopes_supported": []string{"openid", "email", "profile", "email_verified", "given_name", "family_name", "nick_name", "picture"},
"response_modes_supported": []string{"query", "fragment", "form_post"},
"response_modes_supported": []string{"query", "fragment", "form_post", "web_message"},
"id_token_signing_alg_values_supported": []string{jwtType},
"claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "gender", "birthdate", "phone_number", "phone_number_verified"},
"claims_supported": []string{"aud", "exp", "iss", "iat", "sub", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "picture", "email", "email_verified", "roles", "gender", "birthdate", "phone_number", "phone_number_verified", "nonce"},
})
}
}

View File

@@ -22,7 +22,7 @@ import (
func TokenHandler() gin.HandlerFunc {
return func(gc *gin.Context) {
var reqBody map[string]string
if err := gc.BindJSON(&reqBody); err != nil {
if err := gc.Bind(&reqBody); err != nil {
log.Debug("Error binding JSON: ", err)
gc.JSON(http.StatusBadRequest, gin.H{
"error": "error_binding_json",

View File

@@ -58,7 +58,10 @@ func main() {
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.

View File

@@ -57,7 +57,7 @@ func InitMemStore() error {
}
redisURL := requiredEnvs.RedisURL
if redisURL != "" && !requiredEnvs.disableRedisForEnv {
if redisURL != "" && !requiredEnvs.DisableRedisForEnv {
log.Info("Initializing Redis memory store")
Provider, err = redis.NewRedisProvider(redisURL)
if err != nil {

View File

@@ -27,7 +27,7 @@ type RequiredEnv struct {
DatabaseCertKey string `json:"DATABASE_CERT_KEY"`
DatabaseCACert string `json:"DATABASE_CA_CERT"`
RedisURL string `json:"REDIS_URL"`
disableRedisForEnv bool `json:"DISABLE_REDIS_FOR_ENV"`
DisableRedisForEnv bool `json:"DISABLE_REDIS_FOR_ENV"`
}
// RequiredEnvObj is a simple in-memory store for sessions.
@@ -138,7 +138,7 @@ func InitRequiredEnv() error {
DatabaseCertKey: dbCertKey,
DatabaseCACert: dbCACert,
RedisURL: redisURL,
disableRedisForEnv: disableRedisForEnv,
DisableRedisForEnv: disableRedisForEnv,
}
RequiredEnvStoreObj = &RequiredEnvStore{

View File

@@ -179,6 +179,8 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
res.DisableStrongPassword = store[constants.EnvKeyDisableStrongPassword].(bool)
res.EnforceMultiFactorAuthentication = store[constants.EnvKeyEnforceMultiFactorAuthentication].(bool)
res.DisableMultiFactorAuthentication = store[constants.EnvKeyDisableMultiFactorAuthentication].(bool)
res.AdminCookieSecure = store[constants.EnvKeyAdminCookieSecure].(bool)
res.AppCookieSecure = store[constants.EnvKeyAppCookieSecure].(bool)
return res, nil
}

View File

@@ -85,7 +85,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(),

View 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>