Compare commits
24 Commits
0.14.0-bet
...
feat/open-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2bf6b8f91d | ||
![]() |
776c0fba8b | ||
![]() |
dd64aa2e79 | ||
![]() |
157b13baa7 | ||
![]() |
d1e284116d | ||
![]() |
2f9725d8e1 | ||
![]() |
ee7aea7bee | ||
![]() |
5d73df0040 | ||
![]() |
60cd317e67 | ||
![]() |
f5bdc8db39 | ||
![]() |
9eca697a91 | ||
![]() |
7136ee924d | ||
![]() |
fd9eb7c733 | ||
![]() |
917eaeb2ed | ||
![]() |
3bb90acc9e | ||
![]() |
a69b8e290c | ||
![]() |
674eeeea4e | ||
![]() |
8c2bf6ee0d | ||
![]() |
57bc091499 | ||
![]() |
128a2a8f75 | ||
![]() |
7b09a8817c | ||
![]() |
1d61840c6d | ||
![]() |
4b25e8941c | ||
![]() |
136eda15bf |
30
app/package-lock.json
generated
30
app/package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "0.7.0",
|
"@authorizerdev/authorizer-react": "latest",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-js": {
|
"node_modules/@authorizerdev/authorizer-js": {
|
||||||
"version": "0.3.0",
|
"version": "0.4.0-beta.3",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.3.tgz",
|
||||||
"integrity": "sha512-KCE5Dw5MUnEgstBUayBriDQAOjqbxU7ixC00rTHAE6aD6TxJkeSls0vCTXpvt4iiKhFK6q9BhHwa/5NwWYpDBQ==",
|
"integrity": "sha512-OGZc6I6cnpi/WkSotkjVIc3LEzl8pFeiohr8+Db9xWd75/oTfOZqWRuIHTnTc1FC+6Sv2EjTJ9Aa6lrloWG+NQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
},
|
},
|
||||||
@@ -35,11 +35,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-react": {
|
"node_modules/@authorizerdev/authorizer-react": {
|
||||||
"version": "0.7.0",
|
"version": "0.9.0-beta.7",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.7.tgz",
|
||||||
"integrity": "sha512-cAxUhodftIveSQt+rFuEA0CxjmbpVfE43ioZBwBxqWEuJHPdPH7bohOQRgTyA2xb3QVnh7kr607Tau13DO7qUA==",
|
"integrity": "sha512-hCGsVionKMZNk+uD0CLtMIkUzhQqpHbVntko3rY+O7ouOrTrikY/WQVPbo1bqX1cu/6/cHE4RVU3cZ7V5xnxVg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": "^0.3.0",
|
"@authorizerdev/authorizer-js": "^0.4.0-beta.3",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
@@ -829,19 +829,19 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": {
|
"@authorizerdev/authorizer-js": {
|
||||||
"version": "0.3.0",
|
"version": "0.4.0-beta.3",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.3.tgz",
|
||||||
"integrity": "sha512-KCE5Dw5MUnEgstBUayBriDQAOjqbxU7ixC00rTHAE6aD6TxJkeSls0vCTXpvt4iiKhFK6q9BhHwa/5NwWYpDBQ==",
|
"integrity": "sha512-OGZc6I6cnpi/WkSotkjVIc3LEzl8pFeiohr8+Db9xWd75/oTfOZqWRuIHTnTc1FC+6Sv2EjTJ9Aa6lrloWG+NQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@authorizerdev/authorizer-react": {
|
"@authorizerdev/authorizer-react": {
|
||||||
"version": "0.7.0",
|
"version": "0.9.0-beta.7",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.7.tgz",
|
||||||
"integrity": "sha512-cAxUhodftIveSQt+rFuEA0CxjmbpVfE43ioZBwBxqWEuJHPdPH7bohOQRgTyA2xb3QVnh7kr607Tau13DO7qUA==",
|
"integrity": "sha512-hCGsVionKMZNk+uD0CLtMIkUzhQqpHbVntko3rY+O7ouOrTrikY/WQVPbo1bqX1cu/6/cHE4RVU3cZ7V5xnxVg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@authorizerdev/authorizer-js": "^0.3.0",
|
"@authorizerdev/authorizer-js": "^0.4.0-beta.3",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
|
@@ -2,10 +2,33 @@ import React from 'react';
|
|||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
|
||||||
import Root from './Root';
|
import Root from './Root';
|
||||||
|
import { createRandomString } from './utils/common';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
// @ts-ignore
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const globalState: Record<string, string> = window['__authorizer__'];
|
const state = searchParams.get('state') || createRandomString();
|
||||||
|
const scope = searchParams.get('scope')
|
||||||
|
? searchParams.get('scope')?.toString().split(' ')
|
||||||
|
: `openid profile email`;
|
||||||
|
|
||||||
|
const urlProps: Record<string, any> = {
|
||||||
|
state,
|
||||||
|
scope,
|
||||||
|
};
|
||||||
|
|
||||||
|
const redirectURL =
|
||||||
|
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
|
||||||
|
if (redirectURL) {
|
||||||
|
urlProps.redirectURL = redirectURL;
|
||||||
|
} else {
|
||||||
|
urlProps.redirectURL = window.location.origin;
|
||||||
|
}
|
||||||
|
const globalState: Record<string, string> = {
|
||||||
|
// @ts-ignore
|
||||||
|
...window['__authorizer__'],
|
||||||
|
...urlProps,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -30,15 +53,7 @@ export default function App() {
|
|||||||
/>
|
/>
|
||||||
<h1>{globalState.organizationName}</h1>
|
<h1>{globalState.organizationName}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="container">
|
||||||
style={{
|
|
||||||
width: 400,
|
|
||||||
margin: `10px auto`,
|
|
||||||
border: `1px solid #D1D5DB`,
|
|
||||||
padding: `25px 20px`,
|
|
||||||
borderRadius: 5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<AuthorizerProvider
|
<AuthorizerProvider
|
||||||
config={{
|
config={{
|
||||||
@@ -46,7 +61,7 @@ export default function App() {
|
|||||||
redirectURL: globalState.redirectURL,
|
redirectURL: globalState.redirectURL,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Root />
|
<Root globalState={globalState} />
|
||||||
</AuthorizerProvider>
|
</AuthorizerProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -6,14 +6,30 @@ const ResetPassword = lazy(() => import('./pages/rest-password'));
|
|||||||
const Login = lazy(() => import('./pages/login'));
|
const Login = lazy(() => import('./pages/login'));
|
||||||
const Dashboard = lazy(() => import('./pages/dashboard'));
|
const Dashboard = lazy(() => import('./pages/dashboard'));
|
||||||
|
|
||||||
export default function Root() {
|
export default function Root({
|
||||||
|
globalState,
|
||||||
|
}: {
|
||||||
|
globalState: Record<string, string>;
|
||||||
|
}) {
|
||||||
const { token, loading, config } = useAuthorizer();
|
const { token, loading, config } = useAuthorizer();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token) {
|
if (token) {
|
||||||
const url = new URL(config.redirectURL || '/app');
|
let redirectURL = config.redirectURL || '/app';
|
||||||
|
let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
|
||||||
|
if (token.refresh_token) {
|
||||||
|
params += `&refresh_token=${token.refresh_token}`;
|
||||||
|
}
|
||||||
|
const url = new URL(redirectURL);
|
||||||
|
if (redirectURL.includes('?')) {
|
||||||
|
redirectURL = `${redirectURL}&${params}`;
|
||||||
|
} else {
|
||||||
|
redirectURL = `${redirectURL}?${params}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (url.origin !== window.location.origin) {
|
if (url.origin !== window.location.origin) {
|
||||||
window.location.href = config.redirectURL || '/app';
|
sessionStorage.removeItem('authorizer_state');
|
||||||
|
window.location.replace(redirectURL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return () => {};
|
return () => {};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 10;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
sans-serif;
|
sans-serif;
|
||||||
@@ -14,3 +14,17 @@ body {
|
|||||||
*:after {
|
*:after {
|
||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
box-sizing: content-box;
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
padding: 25px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
22
app/src/utils/common.ts
Normal file
22
app/src/utils/common.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export const getCrypto = () => {
|
||||||
|
//ie 11.x uses msCrypto
|
||||||
|
return (window.crypto || (window as any).msCrypto) as Crypto;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createRandomString = () => {
|
||||||
|
const charset =
|
||||||
|
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
|
||||||
|
let random = '';
|
||||||
|
const randomValues = Array.from(
|
||||||
|
getCrypto().getRandomValues(new Uint8Array(43))
|
||||||
|
);
|
||||||
|
randomValues.forEach((v) => (random += charset[v % charset.length]));
|
||||||
|
return random;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createQueryParams = (params: any) => {
|
||||||
|
return Object.keys(params)
|
||||||
|
.filter((k) => typeof params[k] !== 'undefined')
|
||||||
|
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
|
||||||
|
.join('&');
|
||||||
|
};
|
@@ -29,10 +29,11 @@ import {
|
|||||||
} from 'react-icons/fi';
|
} from 'react-icons/fi';
|
||||||
import { IconType } from 'react-icons';
|
import { IconType } from 'react-icons';
|
||||||
import { ReactText } from 'react';
|
import { ReactText } from 'react';
|
||||||
import { useMutation } from 'urql';
|
import { useMutation, useQuery } from 'urql';
|
||||||
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
|
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { useAuthContext } from '../contexts/AuthContext';
|
import { useAuthContext } from '../contexts/AuthContext';
|
||||||
import { AdminLogout } from '../graphql/mutation';
|
import { AdminLogout } from '../graphql/mutation';
|
||||||
|
import { MetaQuery } from '../graphql/queries';
|
||||||
|
|
||||||
interface LinkItemProps {
|
interface LinkItemProps {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -51,6 +52,7 @@ interface SidebarProps extends BoxProps {
|
|||||||
|
|
||||||
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
const [{ fetching, data }] = useQuery({ query: MetaQuery });
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
transition="3s ease"
|
transition="3s ease"
|
||||||
@@ -98,6 +100,19 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
|
|||||||
>
|
>
|
||||||
<NavItem icon={FiCode}>API Playground</NavItem>
|
<NavItem icon={FiCode}>API Playground</NavItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
{data?.meta?.version && (
|
||||||
|
<Text
|
||||||
|
color="gray.600"
|
||||||
|
fontSize="sm"
|
||||||
|
textAlign="center"
|
||||||
|
position="absolute"
|
||||||
|
bottom="5"
|
||||||
|
left="7"
|
||||||
|
>
|
||||||
|
Current Version: {data.meta.version}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,12 @@
|
|||||||
|
export const MetaQuery = `
|
||||||
|
query MetaQuery {
|
||||||
|
meta {
|
||||||
|
version
|
||||||
|
client_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const AdminSessionQuery = `
|
export const AdminSessionQuery = `
|
||||||
query {
|
query {
|
||||||
_admin_session{
|
_admin_session{
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
import { Box, Center, Flex, Image, Text } from '@chakra-ui/react';
|
import { Box, Flex, Image, Text, Spinner } from '@chakra-ui/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { LOGO_URL } from '../constants';
|
import { useQuery } from 'urql';
|
||||||
|
import { MetaQuery } from '../graphql/queries';
|
||||||
|
|
||||||
export function AuthLayout({ children }: { children: React.ReactNode }) {
|
export function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
const [{ fetching, data }] = useQuery({ query: MetaQuery });
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
flexWrap="wrap"
|
flexWrap="wrap"
|
||||||
@@ -23,9 +25,18 @@ export function AuthLayout({ children }: { children: React.ReactNode }) {
|
|||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
|
{fetching ? (
|
||||||
{children}
|
<Spinner />
|
||||||
</Box>
|
) : (
|
||||||
|
<>
|
||||||
|
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
<Text color="gray.600" fontSize="sm">
|
||||||
|
Current Version: {data.meta.version}
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,6 @@ import {
|
|||||||
useToast,
|
useToast,
|
||||||
VStack,
|
VStack,
|
||||||
Text,
|
Text,
|
||||||
Divider,
|
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useMutation } from 'urql';
|
import { useMutation } from 'urql';
|
||||||
|
@@ -3,12 +3,14 @@ package crypto
|
|||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bytes = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
|
var bytes = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 0o5}
|
||||||
|
|
||||||
// EncryptAES method is to encrypt or hide any classified text
|
// EncryptAES method is to encrypt or hide any classified text
|
||||||
func EncryptAES(text string) (string, error) {
|
func EncryptAES(text string) (string, error) {
|
||||||
@@ -40,3 +42,67 @@ func DecryptAES(text string) (string, error) {
|
|||||||
cfb.XORKeyStream(plainText, []byte(cipherText))
|
cfb.XORKeyStream(plainText, []byte(cipherText))
|
||||||
return string(plainText), nil
|
return string(plainText), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncryptAESEnv encrypts data using AES algorithm
|
||||||
|
// kept for the backward compatibility of env data encryption
|
||||||
|
func EncryptAESEnv(text []byte) ([]byte, error) {
|
||||||
|
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
var res []byte
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcm or Galois/Counter Mode, is a mode of operation
|
||||||
|
// for symmetric key cryptographic block ciphers
|
||||||
|
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
|
||||||
|
gcm, err := cipher.NewGCM(c)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates a new byte array the size of the nonce
|
||||||
|
// which must be passed to Seal
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
// populates our nonce with a cryptographically secure
|
||||||
|
// random sequence
|
||||||
|
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// here we encrypt our text using the Seal function
|
||||||
|
// Seal encrypts and authenticates plaintext, authenticates the
|
||||||
|
// additional data and appends the result to dst, returning the updated
|
||||||
|
// slice. The nonce must be NonceSize() bytes long and unique for all
|
||||||
|
// time, for a given key.
|
||||||
|
return gcm.Seal(nonce, nonce, text, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptAES decrypts data using AES algorithm
|
||||||
|
// Kept for the backward compatibility of env data decryption
|
||||||
|
func DecryptAESEnv(ciphertext []byte) ([]byte, error) {
|
||||||
|
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
var res []byte
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cipher.NewGCM(c)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonceSize := gcm.NonceSize()
|
||||||
|
if len(ciphertext) < nonceSize {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||||
|
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
||||||
|
@@ -94,12 +94,13 @@ func EncryptEnvData(data envstore.Store) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
encryptedConfig, err := EncryptAES(string(configData))
|
|
||||||
|
encryptedConfig, err := EncryptAESEnv(configData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(encryptedConfig), nil
|
return EncryptB64(string(encryptedConfig)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptPassword is used for encrypting password
|
// EncryptPassword is used for encrypting password
|
||||||
|
@@ -4,25 +4,28 @@ import "github.com/authorizerdev/authorizer/server/graph/model"
|
|||||||
|
|
||||||
// VerificationRequest model for db
|
// VerificationRequest model for db
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
|
||||||
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id"`
|
||||||
Token string `gorm:"type:text" json:"token" bson:"token"`
|
Token string `gorm:"type:text" json:"token" bson:"token"`
|
||||||
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
Identifier string `gorm:"uniqueIndex:idx_email_identifier" json:"identifier" bson:"identifier"`
|
||||||
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
ExpiresAt int64 `json:"expires_at" bson:"expires_at"`
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||||
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
||||||
Nonce string `gorm:"type:char(36)" json:"nonce" bson:"nonce"`
|
Nonce string `gorm:"type:text" json:"nonce" bson:"nonce"`
|
||||||
|
RedirectURI string `gorm:"type:text" json:"redirect_uri" bson:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||||
return &model.VerificationRequest{
|
return &model.VerificationRequest{
|
||||||
ID: v.ID,
|
ID: v.ID,
|
||||||
Token: &v.Token,
|
Token: &v.Token,
|
||||||
Identifier: &v.Identifier,
|
Identifier: &v.Identifier,
|
||||||
Expires: &v.ExpiresAt,
|
Expires: &v.ExpiresAt,
|
||||||
CreatedAt: &v.CreatedAt,
|
CreatedAt: &v.CreatedAt,
|
||||||
UpdatedAt: &v.UpdatedAt,
|
UpdatedAt: &v.UpdatedAt,
|
||||||
Email: &v.Email,
|
Email: &v.Email,
|
||||||
|
Nonce: &v.Nonce,
|
||||||
|
RedirectURI: &v.RedirectURI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ func (p *provider) AddVerificationRequest(verificationRequest models.Verificatio
|
|||||||
verificationRequest.UpdatedAt = time.Now().Unix()
|
verificationRequest.UpdatedAt = time.Now().Unix()
|
||||||
result := p.db.Clauses(clause.OnConflict{
|
result := p.db.Clauses(clause.OnConflict{
|
||||||
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
|
Columns: []clause.Column{{Name: "email"}, {Name: "identifier"}},
|
||||||
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at"}),
|
DoUpdates: clause.AssignmentColumns([]string{"token", "expires_at", "nonce", "redirect_uri"}),
|
||||||
}).Create(&verificationRequest)
|
}).Create(&verificationRequest)
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package email
|
package email
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
)
|
)
|
||||||
@@ -103,5 +105,9 @@ func SendVerificationMail(toEmail, token, hostname string) error {
|
|||||||
message = addEmailTemplate(message, data, "verify_email.tmpl")
|
message = addEmailTemplate(message, data, "verify_email.tmpl")
|
||||||
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
// bodyMessage := sender.WriteHTMLEmail(Receiver, Subject, message)
|
||||||
|
|
||||||
return SendMail(Receiver, Subject, message)
|
err := SendMail(Receiver, Subject, message)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("=> error sending email:", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
20
server/env/persist_env.go
vendored
20
server/env/persist_env.go
vendored
@@ -34,12 +34,17 @@ func GetEnvData() (envstore.Store, error) {
|
|||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||||
|
|
||||||
decryptedConfigs, err := crypto.DecryptAES(env.EnvData)
|
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(decryptedConfigs), &result)
|
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(decryptedConfigs, &result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
@@ -82,7 +87,12 @@ func PersistEnv() error {
|
|||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||||
|
|
||||||
decryptedConfigs, err := crypto.DecryptAES(env.EnvData)
|
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptedConfigs, err := crypto.DecryptAESEnv([]byte(b64DecryptedConfig))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -90,7 +100,7 @@ func PersistEnv() error {
|
|||||||
// temp store variable
|
// temp store variable
|
||||||
var storeData envstore.Store
|
var storeData envstore.Store
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(decryptedConfigs), &storeData)
|
err = json.Unmarshal(decryptedConfigs, &storeData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -102,7 +112,7 @@ func PersistEnv() error {
|
|||||||
|
|
||||||
for key, value := range storeData.StringEnv {
|
for key, value := range storeData.StringEnv {
|
||||||
// don't override unexposed envs
|
// don't override unexposed envs
|
||||||
if key != constants.EnvKeyEncryptionKey && key != constants.EnvKeyClientID && key != constants.EnvKeyClientSecret && key != constants.EnvKeyJWK {
|
if key != constants.EnvKeyEncryptionKey {
|
||||||
// check only for derivative keys
|
// check only for derivative keys
|
||||||
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
// No need to check for ENCRYPTION_KEY which special key we use for encrypting config data
|
||||||
// as we have removed it from json
|
// as we have removed it from json
|
||||||
|
@@ -119,6 +119,7 @@ type ComplexityRoot struct {
|
|||||||
MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int
|
MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int
|
||||||
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
||||||
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
||||||
|
Revoke func(childComplexity int, params model.OAuthRevokeInput) int
|
||||||
Signup func(childComplexity int, params model.SignUpInput) int
|
Signup func(childComplexity int, params model.SignUpInput) int
|
||||||
UpdateEnv func(childComplexity int, params model.UpdateEnvInput) int
|
UpdateEnv func(childComplexity int, params model.UpdateEnvInput) int
|
||||||
UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int
|
UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int
|
||||||
@@ -173,13 +174,15 @@ type ComplexityRoot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VerificationRequest struct {
|
VerificationRequest struct {
|
||||||
CreatedAt func(childComplexity int) int
|
CreatedAt func(childComplexity int) int
|
||||||
Email func(childComplexity int) int
|
Email func(childComplexity int) int
|
||||||
Expires func(childComplexity int) int
|
Expires func(childComplexity int) int
|
||||||
ID func(childComplexity int) int
|
ID func(childComplexity int) int
|
||||||
Identifier func(childComplexity int) int
|
Identifier func(childComplexity int) int
|
||||||
Token func(childComplexity int) int
|
Nonce func(childComplexity int) int
|
||||||
UpdatedAt func(childComplexity int) int
|
RedirectURI func(childComplexity int) int
|
||||||
|
Token func(childComplexity int) int
|
||||||
|
UpdatedAt func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
VerificationRequests struct {
|
VerificationRequests struct {
|
||||||
@@ -198,6 +201,7 @@ type MutationResolver interface {
|
|||||||
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
|
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
|
||||||
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error)
|
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error)
|
||||||
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
|
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
|
||||||
|
Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error)
|
||||||
DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error)
|
DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error)
|
||||||
UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error)
|
UpdateUser(ctx context.Context, params model.UpdateUserInput) (*model.User, error)
|
||||||
AdminSignup(ctx context.Context, params model.AdminSignupInput) (*model.Response, error)
|
AdminSignup(ctx context.Context, params model.AdminSignupInput) (*model.Response, error)
|
||||||
@@ -711,6 +715,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Mutation.ResetPassword(childComplexity, args["params"].(model.ResetPasswordInput)), true
|
return e.complexity.Mutation.ResetPassword(childComplexity, args["params"].(model.ResetPasswordInput)), true
|
||||||
|
|
||||||
|
case "Mutation.revoke":
|
||||||
|
if e.complexity.Mutation.Revoke == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_revoke_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.Revoke(childComplexity, args["params"].(model.OAuthRevokeInput)), true
|
||||||
|
|
||||||
case "Mutation.signup":
|
case "Mutation.signup":
|
||||||
if e.complexity.Mutation.Signup == nil {
|
if e.complexity.Mutation.Signup == nil {
|
||||||
break
|
break
|
||||||
@@ -1038,6 +1054,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.VerificationRequest.Identifier(childComplexity), true
|
return e.complexity.VerificationRequest.Identifier(childComplexity), true
|
||||||
|
|
||||||
|
case "VerificationRequest.nonce":
|
||||||
|
if e.complexity.VerificationRequest.Nonce == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.VerificationRequest.Nonce(childComplexity), true
|
||||||
|
|
||||||
|
case "VerificationRequest.redirect_uri":
|
||||||
|
if e.complexity.VerificationRequest.RedirectURI == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.VerificationRequest.RedirectURI(childComplexity), true
|
||||||
|
|
||||||
case "VerificationRequest.token":
|
case "VerificationRequest.token":
|
||||||
if e.complexity.VerificationRequest.Token == nil {
|
if e.complexity.VerificationRequest.Token == nil {
|
||||||
break
|
break
|
||||||
@@ -1189,6 +1219,8 @@ type VerificationRequest {
|
|||||||
expires: Int64
|
expires: Int64
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
|
nonce: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequests {
|
type VerificationRequests {
|
||||||
@@ -1311,6 +1343,7 @@ input SignUpInput {
|
|||||||
password: String!
|
password: String!
|
||||||
confirm_password: String!
|
confirm_password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
|
scope: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input LoginInput {
|
input LoginInput {
|
||||||
@@ -1361,6 +1394,8 @@ input UpdateUserInput {
|
|||||||
|
|
||||||
input ForgotPasswordInput {
|
input ForgotPasswordInput {
|
||||||
email: String!
|
email: String!
|
||||||
|
state: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResetPasswordInput {
|
input ResetPasswordInput {
|
||||||
@@ -1377,6 +1412,8 @@ input MagicLinkLoginInput {
|
|||||||
email: String!
|
email: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
|
state: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input SessionQueryInput {
|
input SessionQueryInput {
|
||||||
@@ -1393,6 +1430,10 @@ input PaginatedInput {
|
|||||||
pagination: PaginationInput
|
pagination: PaginationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input OAuthRevokeInput {
|
||||||
|
refresh_token: String!
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
@@ -1403,6 +1444,7 @@ type Mutation {
|
|||||||
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
||||||
forgot_password(params: ForgotPasswordInput!): Response!
|
forgot_password(params: ForgotPasswordInput!): Response!
|
||||||
reset_password(params: ResetPasswordInput!): Response!
|
reset_password(params: ResetPasswordInput!): Response!
|
||||||
|
revoke(params: OAuthRevokeInput!): Response!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_delete_user(params: DeleteUserInput!): Response!
|
_delete_user(params: DeleteUserInput!): Response!
|
||||||
_update_user(params: UpdateUserInput!): User!
|
_update_user(params: UpdateUserInput!): User!
|
||||||
@@ -1580,6 +1622,21 @@ func (ec *executionContext) field_Mutation_reset_password_args(ctx context.Conte
|
|||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_revoke_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 model.OAuthRevokeInput
|
||||||
|
if tmp, ok := rawArgs["params"]; ok {
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||||
|
arg0, err = ec.unmarshalNOAuthRevokeInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐOAuthRevokeInput(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["params"] = arg0
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
@@ -3838,6 +3895,48 @@ func (ec *executionContext) _Mutation_reset_password(ctx context.Context, field
|
|||||||
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_revoke(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
IsResolver: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
rawArgs := field.ArgumentMap(ec.Variables)
|
||||||
|
args, err := ec.field_Mutation_revoke_args(ctx, rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
fc.Args = args
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Mutation().Revoke(rctx, args["params"].(model.OAuthRevokeInput))
|
||||||
|
})
|
||||||
|
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.(*model.Response)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation__delete_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation__delete_user(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -5451,6 +5550,70 @@ func (ec *executionContext) _VerificationRequest_updated_at(ctx context.Context,
|
|||||||
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
|
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _VerificationRequest_nonce(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "VerificationRequest",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.Nonce, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*string)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _VerificationRequest_redirect_uri(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
ec.Error(ctx, ec.Recover(ctx, r))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "VerificationRequest",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return obj.RedirectURI, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*string)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _VerificationRequests_pagination(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequests) (ret graphql.Marshaler) {
|
func (ec *executionContext) _VerificationRequests_pagination(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequests) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@@ -6729,6 +6892,22 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
|
case "state":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||||
|
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "redirect_uri":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("redirect_uri"))
|
||||||
|
it.RedirectURI, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6815,6 +6994,45 @@ func (ec *executionContext) unmarshalInputMagicLinkLoginInput(ctx context.Contex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
|
case "state":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||||
|
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "redirect_uri":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("redirect_uri"))
|
||||||
|
it.RedirectURI, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalInputOAuthRevokeInput(ctx context.Context, obj interface{}) (model.OAuthRevokeInput, error) {
|
||||||
|
var it model.OAuthRevokeInput
|
||||||
|
asMap := map[string]interface{}{}
|
||||||
|
for k, v := range obj.(map[string]interface{}) {
|
||||||
|
asMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range asMap {
|
||||||
|
switch k {
|
||||||
|
case "refresh_token":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("refresh_token"))
|
||||||
|
it.RefreshToken, err = ec.unmarshalNString2string(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7081,6 +7299,14 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
|
case "scope":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scope"))
|
||||||
|
it.Scope, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7921,6 +8147,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
case "revoke":
|
||||||
|
out.Values[i] = ec._Mutation_revoke(ctx, field)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "_delete_user":
|
case "_delete_user":
|
||||||
out.Values[i] = ec._Mutation__delete_user(ctx, field)
|
out.Values[i] = ec._Mutation__delete_user(ctx, field)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
@@ -8290,6 +8521,10 @@ func (ec *executionContext) _VerificationRequest(ctx context.Context, sel ast.Se
|
|||||||
out.Values[i] = ec._VerificationRequest_created_at(ctx, field, obj)
|
out.Values[i] = ec._VerificationRequest_created_at(ctx, field, obj)
|
||||||
case "updated_at":
|
case "updated_at":
|
||||||
out.Values[i] = ec._VerificationRequest_updated_at(ctx, field, obj)
|
out.Values[i] = ec._VerificationRequest_updated_at(ctx, field, obj)
|
||||||
|
case "nonce":
|
||||||
|
out.Values[i] = ec._VerificationRequest_nonce(ctx, field, obj)
|
||||||
|
case "redirect_uri":
|
||||||
|
out.Values[i] = ec._VerificationRequest_redirect_uri(ctx, field, obj)
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
panic("unknown field " + strconv.Quote(field.Name))
|
||||||
}
|
}
|
||||||
@@ -8700,6 +8935,11 @@ func (ec *executionContext) marshalNMeta2ᚖgithubᚗcomᚋauthorizerdevᚋautho
|
|||||||
return ec._Meta(ctx, sel, v)
|
return ec._Meta(ctx, sel, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNOAuthRevokeInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐOAuthRevokeInput(ctx context.Context, v interface{}) (model.OAuthRevokeInput, error) {
|
||||||
|
res, err := ec.unmarshalInputOAuthRevokeInput(ctx, v)
|
||||||
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) marshalNPagination2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐPagination(ctx context.Context, sel ast.SelectionSet, v *model.Pagination) graphql.Marshaler {
|
func (ec *executionContext) marshalNPagination2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐPagination(ctx context.Context, sel ast.SelectionSet, v *model.Pagination) graphql.Marshaler {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||||
|
@@ -69,7 +69,9 @@ type Error struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ForgotPasswordInput struct {
|
type ForgotPasswordInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
State *string `json:"state"`
|
||||||
|
RedirectURI *string `json:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginInput struct {
|
type LoginInput struct {
|
||||||
@@ -80,9 +82,11 @@ type LoginInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MagicLinkLoginInput struct {
|
type MagicLinkLoginInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
Scope []string `json:"scope"`
|
Scope []string `json:"scope"`
|
||||||
|
State *string `json:"state"`
|
||||||
|
RedirectURI *string `json:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
@@ -96,6 +100,10 @@ type Meta struct {
|
|||||||
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OAuthRevokeInput struct {
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
type PaginatedInput struct {
|
type PaginatedInput struct {
|
||||||
Pagination *PaginationInput `json:"pagination"`
|
Pagination *PaginationInput `json:"pagination"`
|
||||||
}
|
}
|
||||||
@@ -145,6 +153,7 @@ type SignUpInput struct {
|
|||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
ConfirmPassword string `json:"confirm_password"`
|
ConfirmPassword string `json:"confirm_password"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
|
Scope []string `json:"scope"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateEnvInput struct {
|
type UpdateEnvInput struct {
|
||||||
@@ -239,13 +248,15 @@ type Users struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Identifier *string `json:"identifier"`
|
Identifier *string `json:"identifier"`
|
||||||
Token *string `json:"token"`
|
Token *string `json:"token"`
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email"`
|
||||||
Expires *int64 `json:"expires"`
|
Expires *int64 `json:"expires"`
|
||||||
CreatedAt *int64 `json:"created_at"`
|
CreatedAt *int64 `json:"created_at"`
|
||||||
UpdatedAt *int64 `json:"updated_at"`
|
UpdatedAt *int64 `json:"updated_at"`
|
||||||
|
Nonce *string `json:"nonce"`
|
||||||
|
RedirectURI *string `json:"redirect_uri"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequests struct {
|
type VerificationRequests struct {
|
||||||
|
@@ -57,6 +57,8 @@ type VerificationRequest {
|
|||||||
expires: Int64
|
expires: Int64
|
||||||
created_at: Int64
|
created_at: Int64
|
||||||
updated_at: Int64
|
updated_at: Int64
|
||||||
|
nonce: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequests {
|
type VerificationRequests {
|
||||||
@@ -179,6 +181,7 @@ input SignUpInput {
|
|||||||
password: String!
|
password: String!
|
||||||
confirm_password: String!
|
confirm_password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
|
scope: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input LoginInput {
|
input LoginInput {
|
||||||
@@ -229,6 +232,8 @@ input UpdateUserInput {
|
|||||||
|
|
||||||
input ForgotPasswordInput {
|
input ForgotPasswordInput {
|
||||||
email: String!
|
email: String!
|
||||||
|
state: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ResetPasswordInput {
|
input ResetPasswordInput {
|
||||||
@@ -245,6 +250,8 @@ input MagicLinkLoginInput {
|
|||||||
email: String!
|
email: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
scope: [String!]
|
scope: [String!]
|
||||||
|
state: String
|
||||||
|
redirect_uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input SessionQueryInput {
|
input SessionQueryInput {
|
||||||
@@ -261,6 +268,10 @@ input PaginatedInput {
|
|||||||
pagination: PaginationInput
|
pagination: PaginationInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input OAuthRevokeInput {
|
||||||
|
refresh_token: String!
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
@@ -271,6 +282,7 @@ type Mutation {
|
|||||||
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
resend_verify_email(params: ResendVerifyEmailInput!): Response!
|
||||||
forgot_password(params: ForgotPasswordInput!): Response!
|
forgot_password(params: ForgotPasswordInput!): Response!
|
||||||
reset_password(params: ResetPasswordInput!): Response!
|
reset_password(params: ResetPasswordInput!): Response!
|
||||||
|
revoke(params: OAuthRevokeInput!): Response!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_delete_user(params: DeleteUserInput!): Response!
|
_delete_user(params: DeleteUserInput!): Response!
|
||||||
_update_user(params: UpdateUserInput!): User!
|
_update_user(params: UpdateUserInput!): User!
|
||||||
|
@@ -47,6 +47,10 @@ func (r *mutationResolver) ResetPassword(ctx context.Context, params model.Reset
|
|||||||
return resolvers.ResetPasswordResolver(ctx, params)
|
return resolvers.ResetPasswordResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error) {
|
||||||
|
return resolvers.RevokeResolver(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
func (r *mutationResolver) DeleteUser(ctx context.Context, params model.DeleteUserInput) (*model.Response, error) {
|
||||||
return resolvers.DeleteUserResolver(ctx, params)
|
return resolvers.DeleteUserResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,11 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -29,44 +27,25 @@ func AppHandler() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state := c.Query("state")
|
redirect_uri := strings.TrimSpace(c.Query("redirect_uri"))
|
||||||
|
state := strings.TrimSpace(c.Query("state"))
|
||||||
|
scopeString := strings.TrimSpace(c.Query("scope"))
|
||||||
|
|
||||||
var stateObj State
|
var scope []string
|
||||||
|
if scopeString == "" {
|
||||||
if state == "" {
|
scope = []string{"openid", "profile", "email"}
|
||||||
stateObj.AuthorizerURL = hostname
|
|
||||||
stateObj.RedirectURL = hostname + "/app"
|
|
||||||
} else {
|
} else {
|
||||||
decodedState, err := crypto.DecryptB64(state)
|
scope = strings.Split(scopeString, " ")
|
||||||
if err != nil {
|
}
|
||||||
c.JSON(400, gin.H{"error": "[unable to decode state] invalid state"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(decodedState), &stateObj)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(400, gin.H{"error": "[unable to parse state] invalid state"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
stateObj.AuthorizerURL = strings.TrimSuffix(stateObj.AuthorizerURL, "/")
|
|
||||||
stateObj.RedirectURL = strings.TrimSuffix(stateObj.RedirectURL, "/")
|
|
||||||
|
|
||||||
|
if redirect_uri == "" {
|
||||||
|
redirect_uri = hostname + "/app"
|
||||||
|
} else {
|
||||||
// validate redirect url with allowed origins
|
// validate redirect url with allowed origins
|
||||||
if !utils.IsValidOrigin(stateObj.RedirectURL) {
|
if !utils.IsValidOrigin(redirect_uri) {
|
||||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if stateObj.AuthorizerURL == "" {
|
|
||||||
c.JSON(400, gin.H{"error": "invalid authorizer url"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate host and domain of authorizer url
|
|
||||||
if strings.TrimSuffix(stateObj.AuthorizerURL, "/") != hostname {
|
|
||||||
c.JSON(400, gin.H{"error": "invalid host url"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug the request state
|
// debug the request state
|
||||||
@@ -77,9 +56,11 @@ func AppHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
c.HTML(http.StatusOK, "app.tmpl", gin.H{
|
||||||
"data": map[string]string{
|
"data": map[string]interface{}{
|
||||||
"authorizerURL": stateObj.AuthorizerURL,
|
"authorizerURL": hostname,
|
||||||
"redirectURL": stateObj.RedirectURL,
|
"redirectURL": redirect_uri,
|
||||||
|
"scope": scope,
|
||||||
|
"state": state,
|
||||||
"organizationName": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName),
|
"organizationName": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationName),
|
||||||
"organizationLogo": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo),
|
"organizationLogo": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyOrganizationLogo),
|
||||||
},
|
},
|
||||||
|
@@ -2,6 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
@@ -17,6 +18,7 @@ import (
|
|||||||
// AuthorizeHandler is the handler for the /authorize route
|
// AuthorizeHandler is the handler for the /authorize route
|
||||||
// required params
|
// required params
|
||||||
// ?redirect_uri = redirect url
|
// ?redirect_uri = redirect url
|
||||||
|
// ?response_mode = to decide if result should be html or re-direct
|
||||||
// state[recommended] = to prevent CSRF attack (for authorizer its compulsory)
|
// state[recommended] = to prevent CSRF attack (for authorizer its compulsory)
|
||||||
// code_challenge = to prevent CSRF attack
|
// code_challenge = to prevent CSRF attack
|
||||||
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
|
// code_challenge_method = to prevent CSRF attack [only sh256 is supported]
|
||||||
@@ -31,62 +33,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
scopeString := strings.TrimSpace(gc.Query("scope"))
|
scopeString := strings.TrimSpace(gc.Query("scope"))
|
||||||
clientID := strings.TrimSpace(gc.Query("client_id"))
|
clientID := strings.TrimSpace(gc.Query("client_id"))
|
||||||
template := "authorize.tmpl"
|
template := "authorize.tmpl"
|
||||||
|
responseMode := strings.TrimSpace(gc.Query("response_mode"))
|
||||||
if 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 clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
|
|
||||||
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 redirectURI == "" {
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
|
||||||
"target_origin": redirectURI,
|
|
||||||
"authorization_response": map[string]interface{}{
|
|
||||||
"type": "authorization_response",
|
|
||||||
"response": map[string]string{
|
|
||||||
"error": "redirect_uri is required",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if 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"
|
|
||||||
}
|
|
||||||
|
|
||||||
var scope []string
|
var scope []string
|
||||||
if scopeString == "" {
|
if scopeString == "" {
|
||||||
@@ -95,80 +42,171 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
scope = strings.Split(scopeString, " ")
|
scope = strings.Split(scopeString, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if responseMode == "" {
|
||||||
|
responseMode = "query"
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseMode != "query" && responseMode != "web_message" {
|
||||||
|
gc.JSON(400, gin.H{"error": "invalid response mode"})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
|
||||||
|
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": "invalid_client_id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if state == "" {
|
||||||
|
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": "state is required",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseType == "" {
|
||||||
|
responseType = "token"
|
||||||
|
}
|
||||||
|
|
||||||
isResponseTypeCode := responseType == "code"
|
isResponseTypeCode := responseType == "code"
|
||||||
isResponseTypeToken := responseType == "token"
|
isResponseTypeToken := responseType == "token"
|
||||||
|
|
||||||
if !isResponseTypeCode && !isResponseTypeToken {
|
if !isResponseTypeCode && !isResponseTypeToken {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "response_type is invalid",
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "response_type is invalid",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isResponseTypeCode {
|
if isResponseTypeCode {
|
||||||
if codeChallenge == "" {
|
if codeChallenge == "" {
|
||||||
gc.HTML(http.StatusBadRequest, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusBadRequest, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "code_challenge is required",
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "code_challenge is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionToken, err := cookie.GetSession(gc)
|
sessionToken, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get session from cookie
|
// get session from cookie
|
||||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userID := claims.Subject
|
userID := claims.Subject
|
||||||
user, err := db.Provider.GetUserByID(userID)
|
user, err := db.Provider.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "signup_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Sign up required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "signup_required",
|
||||||
|
"error_description": "Sign up required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,16 +218,20 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
nonce := uuid.New().String()
|
nonce := uuid.New().String()
|
||||||
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope)
|
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,24 +256,31 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
// rollover the session for security
|
// rollover the session for security
|
||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope)
|
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"authorization_response": map[string]interface{}{
|
} else {
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionstore.RemoveState(sessionToken)
|
sessionstore.RemoveState(sessionToken)
|
||||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := int64(1800)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
res := map[string]interface{}{
|
res := map[string]interface{}{
|
||||||
"access_token": authToken.AccessToken.Token,
|
"access_token": authToken.AccessToken.Token,
|
||||||
"id_token": authToken.IDToken.Token,
|
"id_token": authToken.IDToken.Token,
|
||||||
@@ -243,29 +292,42 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res["refresh_token"] = authToken.RefreshToken.Token
|
res["refresh_token"] = authToken.RefreshToken.Token
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
params += "&refresh_token=" + authToken.RefreshToken.Token
|
||||||
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
if isQuery {
|
||||||
"target_origin": redirectURI,
|
if strings.Contains(redirectURI, "?") {
|
||||||
"authorization_response": map[string]interface{}{
|
gc.Redirect(http.StatusFound, redirectURI+"&"+params)
|
||||||
"type": "authorization_response",
|
} else {
|
||||||
"response": res,
|
gc.Redirect(http.StatusFound, redirectURI+"?"+params)
|
||||||
},
|
}
|
||||||
})
|
} else {
|
||||||
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
|
"target_origin": redirectURI,
|
||||||
|
"authorization_response": map[string]interface{}{
|
||||||
|
"type": "authorization_response",
|
||||||
|
"response": res,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// by default return with error
|
if isQuery {
|
||||||
gc.HTML(http.StatusOK, template, gin.H{
|
gc.Redirect(http.StatusFound, loginURL)
|
||||||
"target_origin": redirectURI,
|
} else {
|
||||||
"authorization_response": map[string]interface{}{
|
// by default return with error
|
||||||
"type": "authorization_response",
|
gc.HTML(http.StatusOK, template, gin.H{
|
||||||
"response": map[string]string{
|
"target_origin": redirectURI,
|
||||||
"error": "login_required",
|
"authorization_response": map[string]interface{}{
|
||||||
"error_description": "Login is required",
|
"type": "authorization_response",
|
||||||
|
"response": map[string]string{
|
||||||
|
"error": "login_required",
|
||||||
|
"error_description": "Login is required",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
"github.com/authorizerdev/authorizer/server/crypto"
|
||||||
@@ -9,8 +10,10 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handler to logout user
|
||||||
func LogoutHandler() gin.HandlerFunc {
|
func LogoutHandler() gin.HandlerFunc {
|
||||||
return func(gc *gin.Context) {
|
return func(gc *gin.Context) {
|
||||||
|
redirectURL := strings.TrimSpace(gc.Query("redirect_uri"))
|
||||||
// get fingerprint hash
|
// get fingerprint hash
|
||||||
fingerprintHash, err := cookie.GetSession(gc)
|
fingerprintHash, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,8 +36,12 @@ func LogoutHandler() gin.HandlerFunc {
|
|||||||
sessionstore.RemoveState(fingerPrint)
|
sessionstore.RemoveState(fingerPrint)
|
||||||
cookie.DeleteSession(gc)
|
cookie.DeleteSession(gc)
|
||||||
|
|
||||||
gc.JSON(http.StatusOK, gin.H{
|
if redirectURL != "" {
|
||||||
"message": "Logged out successfully",
|
gc.Redirect(http.StatusFound, redirectURL)
|
||||||
})
|
} else {
|
||||||
|
gc.JSON(http.StatusOK, gin.H{
|
||||||
|
"message": "Logged out successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -21,7 +22,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,14 +39,15 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
// contains random token, redirect url, role
|
// contains random token, redirect url, role
|
||||||
sessionSplit := strings.Split(state, "___")
|
sessionSplit := strings.Split(state, "___")
|
||||||
|
|
||||||
// TODO validate redirect url
|
if len(sessionSplit) < 3 {
|
||||||
if len(sessionSplit) < 2 {
|
|
||||||
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
c.JSON(400, gin.H{"error": "invalid redirect url"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
inputRoles := strings.Split(sessionSplit[2], ",")
|
stateValue := sessionSplit[0]
|
||||||
redirectURL := sessionSplit[1]
|
redirectURL := sessionSplit[1]
|
||||||
|
inputRoles := strings.Split(sessionSplit[2], ",")
|
||||||
|
scopes := strings.Split(sessionSplit[3], ",")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
user := models.User{}
|
user := models.User{}
|
||||||
@@ -145,17 +146,29 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO use query param
|
authToken, err := token.CreateAuthToken(c, user, inputRoles, scopes)
|
||||||
scope := []string{"openid", "email", "profile"}
|
|
||||||
nonce := uuid.New().String()
|
|
||||||
_, newSessionToken, err := token.CreateSessionToken(user, nonce, inputRoles, scope)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, gin.H{"error": err.Error()})
|
c.JSON(500, gin.H{"error": err.Error()})
|
||||||
}
|
}
|
||||||
|
expiresIn := int64(1800)
|
||||||
|
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token
|
||||||
|
|
||||||
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
|
||||||
|
if authToken.RefreshToken != nil {
|
||||||
|
params = params + `&refresh_token=` + authToken.RefreshToken.Token
|
||||||
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
}
|
||||||
|
|
||||||
sessionstore.SetState(newSessionToken, nonce+"@"+user.ID)
|
|
||||||
cookie.SetSession(c, newSessionToken)
|
|
||||||
go utils.SaveSessionInDB(c, user.ID)
|
go utils.SaveSessionInDB(c, user.ID)
|
||||||
|
if strings.Contains(redirectURL, "?") {
|
||||||
|
redirectURL = redirectURL + "&" + params
|
||||||
|
} else {
|
||||||
|
redirectURL = redirectURL + "?" + params
|
||||||
|
}
|
||||||
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,23 +10,38 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
|
// OAuthLoginHandler set host in the oauth state that is useful for redirecting to oauth_callback
|
||||||
func OAuthLoginHandler() gin.HandlerFunc {
|
func OAuthLoginHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
hostname := utils.GetHost(c)
|
hostname := utils.GetHost(c)
|
||||||
redirectURL := c.Query("redirectURL")
|
redirectURI := strings.TrimSpace(c.Query("redirectURL"))
|
||||||
roles := c.Query("roles")
|
roles := strings.TrimSpace(c.Query("roles"))
|
||||||
|
state := strings.TrimSpace(c.Query("state"))
|
||||||
|
scopeString := strings.TrimSpace(c.Query("scope"))
|
||||||
|
|
||||||
if redirectURL == "" {
|
if redirectURI == "" {
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
"error": "invalid redirect url",
|
"error": "invalid redirect uri",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state == "" {
|
||||||
|
c.JSON(400, gin.H{
|
||||||
|
"error": "invalid state",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var scope []string
|
||||||
|
if scopeString == "" {
|
||||||
|
scope = []string{"openid", "profile", "email"}
|
||||||
|
} else {
|
||||||
|
scope = strings.Split(scopeString, " ")
|
||||||
|
}
|
||||||
|
|
||||||
if roles != "" {
|
if roles != "" {
|
||||||
// validate role
|
// validate role
|
||||||
rolesSplit := strings.Split(roles, ",")
|
rolesSplit := strings.Split(roles, ",")
|
||||||
@@ -43,8 +58,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
|||||||
roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
roles = strings.Join(envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles), ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid := uuid.New()
|
oauthStateString := state + "___" + redirectURI + "___" + roles + "___" + strings.Join(scope, ",")
|
||||||
oauthStateString := uuid.String() + "___" + redirectURL + "___" + roles
|
|
||||||
|
|
||||||
provider := c.Param("oauth_provider")
|
provider := c.Param("oauth_provider")
|
||||||
isProviderConfigured := true
|
isProviderConfigured := true
|
||||||
|
50
server/handlers/revoke.go
Normal file
50
server/handlers/revoke.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Revoke handler to revoke refresh token
|
||||||
|
func RevokeHandler() gin.HandlerFunc {
|
||||||
|
return func(gc *gin.Context) {
|
||||||
|
var reqBody map[string]string
|
||||||
|
if err := gc.BindJSON(&reqBody); err != nil {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "error_binding_json",
|
||||||
|
"error_description": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// get fingerprint hash
|
||||||
|
refreshToken := strings.TrimSpace(reqBody["refresh_token"])
|
||||||
|
clientID := strings.TrimSpace(reqBody["client_id"])
|
||||||
|
|
||||||
|
if clientID == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "client_id_required",
|
||||||
|
"error_description": "The client id is required",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientID != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_client_id",
|
||||||
|
"error_description": "The client id is invalid",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionstore.RemoveState(refreshToken)
|
||||||
|
|
||||||
|
gc.JSON(http.StatusOK, gin.H{
|
||||||
|
"message": "Token revoked successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -15,6 +15,8 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TokenHandler to handle /oauth/token requests
|
||||||
|
// grant type required
|
||||||
func TokenHandler() gin.HandlerFunc {
|
func TokenHandler() gin.HandlerFunc {
|
||||||
return func(gc *gin.Context) {
|
return func(gc *gin.Context) {
|
||||||
var reqBody map[string]string
|
var reqBody map[string]string
|
||||||
@@ -29,6 +31,22 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
codeVerifier := strings.TrimSpace(reqBody["code_verifier"])
|
codeVerifier := strings.TrimSpace(reqBody["code_verifier"])
|
||||||
code := strings.TrimSpace(reqBody["code"])
|
code := strings.TrimSpace(reqBody["code"])
|
||||||
clientID := strings.TrimSpace(reqBody["client_id"])
|
clientID := strings.TrimSpace(reqBody["client_id"])
|
||||||
|
grantType := strings.TrimSpace(reqBody["grant_type"])
|
||||||
|
refreshToken := strings.TrimSpace(reqBody["refresh_token"])
|
||||||
|
|
||||||
|
if grantType == "" {
|
||||||
|
grantType = "authorization_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
isRefreshTokenGrant := grantType == "refresh_token"
|
||||||
|
isAuthorizationCodeGrant := grantType == "authorization_code"
|
||||||
|
|
||||||
|
if !isRefreshTokenGrant && !isAuthorizationCodeGrant {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_grant_type",
|
||||||
|
"error_description": "grant_type is invalid",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if clientID == "" {
|
if clientID == "" {
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
@@ -46,58 +64,95 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if codeVerifier == "" {
|
var userID string
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
var roles, scope []string
|
||||||
"error": "invalid_code_verifier",
|
if isAuthorizationCodeGrant {
|
||||||
"error_description": "The code verifier is required",
|
|
||||||
})
|
if codeVerifier == "" {
|
||||||
return
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_code_verifier",
|
||||||
|
"error_description": "The code verifier is required",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if code == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_code",
|
||||||
|
"error_description": "The code is required",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write([]byte(codeVerifier))
|
||||||
|
encryptedCode := strings.ReplaceAll(base64.URLEncoding.EncodeToString(hash.Sum(nil)), "+", "-")
|
||||||
|
encryptedCode = strings.ReplaceAll(encryptedCode, "/", "_")
|
||||||
|
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
|
||||||
|
sessionData := sessionstore.GetState(encryptedCode)
|
||||||
|
if sessionData == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_code_verifier",
|
||||||
|
"error_description": "The code verifier is invalid",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// split session data
|
||||||
|
// it contains code@sessiontoken
|
||||||
|
sessionDataSplit := strings.Split(sessionData, "@")
|
||||||
|
|
||||||
|
if sessionDataSplit[0] != code {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_code_verifier",
|
||||||
|
"error_description": "The code verifier is invalid",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// rollover the session for security
|
||||||
|
sessionstore.RemoveState(sessionDataSplit[1])
|
||||||
|
// validate session
|
||||||
|
claims, err := token.ValidateBrowserSession(gc, sessionDataSplit[1])
|
||||||
|
if err != nil {
|
||||||
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
|
"error": "unauthorized",
|
||||||
|
"error_description": "Invalid session data",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userID = claims.Subject
|
||||||
|
roles = claims.Roles
|
||||||
|
scope = claims.Scope
|
||||||
|
} else {
|
||||||
|
// validate refresh token
|
||||||
|
if refreshToken == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_refresh_token",
|
||||||
|
"error_description": "The refresh token is invalid",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, err := token.ValidateRefreshToken(gc, refreshToken)
|
||||||
|
if err != nil {
|
||||||
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
|
"error": "unauthorized",
|
||||||
|
"error_description": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
userID = claims["sub"].(string)
|
||||||
|
rolesInterface := claims["roles"].([]interface{})
|
||||||
|
scopeInterface := claims["scope"].([]interface{})
|
||||||
|
for _, v := range rolesInterface {
|
||||||
|
roles = append(roles, v.(string))
|
||||||
|
}
|
||||||
|
for _, v := range scopeInterface {
|
||||||
|
scope = append(scope, v.(string))
|
||||||
|
}
|
||||||
|
// remove older refresh token and rotate it for security
|
||||||
|
sessionstore.RemoveState(refreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
if code == "" {
|
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"error": "invalid_code",
|
|
||||||
"error_description": "The code is required",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := sha256.New()
|
|
||||||
hash.Write([]byte(codeVerifier))
|
|
||||||
encryptedCode := strings.ReplaceAll(base64.URLEncoding.EncodeToString(hash.Sum(nil)), "+", "-")
|
|
||||||
encryptedCode = strings.ReplaceAll(encryptedCode, "/", "_")
|
|
||||||
encryptedCode = strings.ReplaceAll(encryptedCode, "=", "")
|
|
||||||
sessionData := sessionstore.GetState(encryptedCode)
|
|
||||||
if sessionData == "" {
|
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"error": "invalid_code_verifier",
|
|
||||||
"error_description": "The code verifier is invalid",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// split session data
|
|
||||||
// it contains code@sessiontoken
|
|
||||||
sessionDataSplit := strings.Split(sessionData, "@")
|
|
||||||
|
|
||||||
if sessionDataSplit[0] != code {
|
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
|
||||||
"error": "invalid_code_verifier",
|
|
||||||
"error_description": "The code verifier is invalid",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate session
|
|
||||||
claims, err := token.ValidateBrowserSession(gc, sessionDataSplit[1])
|
|
||||||
if err != nil {
|
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
|
||||||
"error": "unauthorized",
|
|
||||||
"error_description": "Invalid session data",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
userID := claims.Subject
|
|
||||||
user, err := db.Provider.GetUserByID(userID)
|
user, err := db.Provider.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
@@ -106,9 +161,8 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// rollover the session for security
|
|
||||||
sessionstore.RemoveState(sessionDataSplit[1])
|
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, claims.Scope)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
"error": "unauthorized",
|
"error": "unauthorized",
|
||||||
@@ -124,13 +178,14 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
res := map[string]interface{}{
|
res := map[string]interface{}{
|
||||||
"access_token": authToken.AccessToken.Token,
|
"access_token": authToken.AccessToken.Token,
|
||||||
"id_token": authToken.IDToken.Token,
|
"id_token": authToken.IDToken.Token,
|
||||||
"scope": claims.Scope,
|
"scope": scope,
|
||||||
|
"roles": roles,
|
||||||
"expires_in": expiresIn,
|
"expires_in": expiresIn,
|
||||||
}
|
}
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res["refresh_token"] = authToken.RefreshToken.Token
|
res["refresh_token"] = authToken.RefreshToken.Token
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.JSON(http.StatusOK, res)
|
gc.JSON(http.StatusOK, res)
|
||||||
|
@@ -2,6 +2,7 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,7 +12,6 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// VerifyEmailHandler handles the verify email route.
|
// VerifyEmailHandler handles the verify email route.
|
||||||
@@ -19,7 +19,7 @@ import (
|
|||||||
func VerifyEmailHandler() gin.HandlerFunc {
|
func VerifyEmailHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
errorRes := gin.H{
|
errorRes := gin.H{
|
||||||
"error": "invalid token",
|
"error": "invalid_token",
|
||||||
}
|
}
|
||||||
tokenInQuery := c.Query("token")
|
tokenInQuery := c.Query("token")
|
||||||
if tokenInQuery == "" {
|
if tokenInQuery == "" {
|
||||||
@@ -29,30 +29,24 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
|
|
||||||
verificationRequest, err := db.Provider.GetVerificationRequestByToken(tokenInQuery)
|
verificationRequest, err := db.Provider.GetVerificationRequestByToken(tokenInQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
errorRes["error_description"] = err.Error()
|
||||||
c.JSON(400, errorRes)
|
c.JSON(400, errorRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := utils.GetHost(c)
|
hostname := utils.GetHost(c)
|
||||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
claim, err := token.ParseJWTToken(tokenInQuery, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
||||||
if err != nil {
|
|
||||||
c.JSON(400, gin.H{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
claim, err := token.ParseJWTToken(tokenInQuery, hostname, encryptedNonce, verificationRequest.Email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
errorRes["error_description"] = err.Error()
|
||||||
c.JSON(400, errorRes)
|
c.JSON(400, errorRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, gin.H{
|
errorRes["error_description"] = err.Error()
|
||||||
"message": err.Error(),
|
c.JSON(400, errorRes)
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,21 +59,53 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||||||
// delete from verification table
|
// delete from verification table
|
||||||
db.Provider.DeleteVerificationRequest(verificationRequest)
|
db.Provider.DeleteVerificationRequest(verificationRequest)
|
||||||
|
|
||||||
roles := strings.Split(user.Roles, ",")
|
state := strings.TrimSpace(c.Query("state"))
|
||||||
scope := []string{"openid", "email", "profile"}
|
redirectURL := strings.TrimSpace(c.Query("redirect_uri"))
|
||||||
nonce := uuid.New().String()
|
rolesString := strings.TrimSpace(c.Query("roles"))
|
||||||
_, authToken, err := token.CreateSessionToken(user, nonce, roles, scope)
|
var roles []string
|
||||||
|
if rolesString == "" {
|
||||||
|
roles = strings.Split(user.Roles, ",")
|
||||||
|
} else {
|
||||||
|
roles = strings.Split(rolesString, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeString := strings.TrimSpace(c.Query("scope"))
|
||||||
|
var scope []string
|
||||||
|
if scopeString == "" {
|
||||||
|
scope = []string{"openid", "email", "profile"}
|
||||||
|
} else {
|
||||||
|
scope = strings.Split(scopeString, " ")
|
||||||
|
}
|
||||||
|
authToken, err := token.CreateAuthToken(c, user, roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, gin.H{
|
errorRes["error_description"] = err.Error()
|
||||||
"message": err.Error(),
|
c.JSON(500, errorRes)
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionstore.SetState(authToken, nonce+"@"+user.ID)
|
expiresIn := int64(1800)
|
||||||
cookie.SetSession(c, authToken)
|
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
|
||||||
|
|
||||||
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
|
||||||
|
if authToken.RefreshToken != nil {
|
||||||
|
params = params + `&refresh_token=${refresh_token}`
|
||||||
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if redirectURL == "" {
|
||||||
|
redirectURL = claim["redirect_uri"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(redirectURL, "?") {
|
||||||
|
redirectURL = redirectURL + "&" + params
|
||||||
|
} else {
|
||||||
|
redirectURL = redirectURL + "?" + params
|
||||||
|
}
|
||||||
|
|
||||||
go utils.SaveSessionInDB(c, user.ID)
|
go utils.SaveSessionInDB(c, user.ID)
|
||||||
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, claim["redirect_url"].(string))
|
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,20 +39,26 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash)
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
if params.RedirectURI != nil {
|
||||||
|
redirectURL = *params.RedirectURI
|
||||||
|
}
|
||||||
|
|
||||||
|
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: constants.VerificationTypeForgotPassword,
|
Identifier: constants.VerificationTypeForgotPassword,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -69,8 +69,6 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
|
||||||
|
|
||||||
expiresIn := int64(1800)
|
expiresIn := int64(1800)
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Logged in successfully`,
|
Message: `Logged in successfully`,
|
||||||
@@ -80,12 +78,13 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||||||
User: user.AsAPIUser(),
|
User: user.AsAPIUser(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res.RefreshToken = &authToken.RefreshToken.Token
|
res.RefreshToken = &authToken.RefreshToken.Token
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
go utils.SaveSessionInDB(gc, user.ID)
|
go utils.SaveSessionInDB(gc, user.ID)
|
||||||
|
@@ -68,6 +68,9 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
|||||||
// Need to modify roles in this case
|
// Need to modify roles in this case
|
||||||
|
|
||||||
// find the unassigned roles
|
// find the unassigned roles
|
||||||
|
if len(params.Roles) <= 0 {
|
||||||
|
inputRoles = envstore.EnvStoreObj.GetSliceStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||||
|
}
|
||||||
existingRoles := strings.Split(existingUser.Roles, ",")
|
existingRoles := strings.Split(existingUser.Roles, ",")
|
||||||
unasignedRoles := []string{}
|
unasignedRoles := []string{}
|
||||||
for _, ir := range inputRoles {
|
for _, ir := range inputRoles {
|
||||||
@@ -109,24 +112,46 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
|||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||||
// insert verification request
|
// insert verification request
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
redirectURLParams := "&roles=" + strings.Join(inputRoles, ",")
|
||||||
|
if params.State != nil {
|
||||||
|
redirectURLParams = redirectURLParams + "&state=" + *params.State
|
||||||
|
}
|
||||||
|
if params.Scope != nil && len(params.Scope) > 0 {
|
||||||
|
redirectURLParams = redirectURLParams + "&scope=" + strings.Join(params.Scope, " ")
|
||||||
|
}
|
||||||
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
if params.RedirectURI != nil {
|
||||||
|
redirectURL = *params.RedirectURI
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(redirectURL, "?") {
|
||||||
|
redirectURL = redirectURL + "&" + redirectURLParams
|
||||||
|
} else {
|
||||||
|
redirectURL = redirectURL + "?" + redirectURLParams
|
||||||
|
}
|
||||||
|
|
||||||
verificationType := constants.VerificationTypeMagicLinkLogin
|
verificationType := constants.VerificationTypeMagicLinkLogin
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash)
|
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
_, err = db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routing so that we can reduce the api latency
|
||||||
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,20 +44,22 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier, hostname, nonceHash)
|
|
||||||
|
verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier, hostname, nonceHash, verificationRequest.RedirectURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: params.Identifier,
|
Identifier: params.Identifier,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: verificationRequest.RedirectURI,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -37,11 +37,7 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
|
|||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
claim, err := token.ParseJWTToken(params.Token, hostname, encryptedNonce, verificationRequest.Email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, fmt.Errorf(`invalid token`)
|
return res, fmt.Errorf(`invalid token`)
|
||||||
}
|
}
|
||||||
|
16
server/resolvers/revoke.go
Normal file
16
server/resolvers/revoke.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package resolvers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RevokeResolver resolver to revoke refresh token
|
||||||
|
func RevokeResolver(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error) {
|
||||||
|
sessionstore.RemoveState(params.RefreshToken)
|
||||||
|
return &model.Response{
|
||||||
|
Message: "Token revoked",
|
||||||
|
}, nil
|
||||||
|
}
|
@@ -80,7 +80,7 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
|||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res.RefreshToken = &authToken.RefreshToken.Token
|
res.RefreshToken = &authToken.RefreshToken.Token
|
||||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
sessionstore.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
@@ -123,21 +123,23 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||||
// insert verification request
|
// insert verification request
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationType := constants.VerificationTypeBasicAuthSignup
|
verificationType := constants.VerificationTypeBasicAuthSignup
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash)
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
@@ -149,6 +151,9 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scope := []string{"openid", "email", "profile"}
|
scope := []string{"openid", "email", "profile"}
|
||||||
|
if params.Scope != nil && len(scope) > 0 {
|
||||||
|
scope = params.Scope
|
||||||
|
}
|
||||||
|
|
||||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -129,21 +129,23 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
|||||||
user.EmailVerifiedAt = nil
|
user.EmailVerifiedAt = nil
|
||||||
hasEmailChanged = true
|
hasEmailChanged = true
|
||||||
// insert verification request
|
// insert verification request
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationType := constants.VerificationTypeUpdateEmail
|
verificationType := constants.VerificationTypeUpdateEmail
|
||||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash)
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: newEmail,
|
Email: newEmail,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -101,21 +101,23 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
|||||||
user.Email = newEmail
|
user.Email = newEmail
|
||||||
user.EmailVerifiedAt = nil
|
user.EmailVerifiedAt = nil
|
||||||
// insert verification request
|
// insert verification request
|
||||||
nonce, nonceHash, err := utils.GenerateNonce()
|
_, nonceHash, err := utils.GenerateNonce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
verificationType := constants.VerificationTypeUpdateEmail
|
verificationType := constants.VerificationTypeUpdateEmail
|
||||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash)
|
redirectURL := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)
|
||||||
|
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash, redirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: newEmail,
|
Email: newEmail,
|
||||||
Nonce: nonce,
|
Nonce: nonceHash,
|
||||||
|
RedirectURI: redirectURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
|
@@ -29,11 +29,7 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
|||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
claim, err := token.ParseJWTToken(params.Token, hostname, encryptedNonce, verificationRequest.Email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, fmt.Errorf(`invalid token: %s`, err.Error())
|
return res, fmt.Errorf(`invalid token: %s`, err.Error())
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ func InitRouter() *gin.Engine {
|
|||||||
router.GET("/userinfo", handlers.UserInfoHandler())
|
router.GET("/userinfo", handlers.UserInfoHandler())
|
||||||
router.GET("/logout", handlers.LogoutHandler())
|
router.GET("/logout", handlers.LogoutHandler())
|
||||||
router.POST("/oauth/token", handlers.TokenHandler())
|
router.POST("/oauth/token", handlers.TokenHandler())
|
||||||
|
router.POST("/oauth/revoke", handlers.RevokeHandler())
|
||||||
|
|
||||||
router.LoadHTMLGlob("templates/*")
|
router.LoadHTMLGlob("templates/*")
|
||||||
// login page app related routes.
|
// login page app related routes.
|
||||||
|
@@ -91,7 +91,7 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if utils.StringSliceContains(scope, "offline_access") {
|
if utils.StringSliceContains(scope, "offline_access") {
|
||||||
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, hostname, nonce)
|
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, scope, hostname, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateRefreshToken util to create JWT token
|
// CreateRefreshToken util to create JWT token
|
||||||
func CreateRefreshToken(user models.User, roles []string, hostname, nonce string) (string, int64, error) {
|
func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonce string) (string, int64, error) {
|
||||||
// expires in 1 year
|
// expires in 1 year
|
||||||
expiryBound := time.Hour * 8760
|
expiryBound := time.Hour * 8760
|
||||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||||
@@ -115,6 +115,7 @@ func CreateRefreshToken(user models.User, roles []string, hostname, nonce string
|
|||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeRefreshToken,
|
"token_type": constants.TokenTypeRefreshToken,
|
||||||
"roles": roles,
|
"roles": roles,
|
||||||
|
"scope": scopes,
|
||||||
"nonce": nonce,
|
"nonce": nonce,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +199,36 @@ func ValidateAccessToken(gc *gin.Context, accessToken string) (map[string]interf
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to validate refreshToken
|
||||||
|
func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]interface{}, error) {
|
||||||
|
var res map[string]interface{}
|
||||||
|
|
||||||
|
if refreshToken == "" {
|
||||||
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
}
|
||||||
|
|
||||||
|
savedSession := sessionstore.GetState(refreshToken)
|
||||||
|
if savedSession == "" {
|
||||||
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
}
|
||||||
|
|
||||||
|
savedSessionSplit := strings.Split(savedSession, "@")
|
||||||
|
nonce := savedSessionSplit[0]
|
||||||
|
userID := savedSessionSplit[1]
|
||||||
|
|
||||||
|
hostname := utils.GetHost(gc)
|
||||||
|
res, err := ParseJWTToken(refreshToken, hostname, nonce, userID)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res["token_type"] != constants.TokenTypeRefreshToken {
|
||||||
|
return res, fmt.Errorf(`unauthorized: invalid token type`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionData, error) {
|
func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionData, error) {
|
||||||
if encryptedSession == "" {
|
if encryptedSession == "" {
|
||||||
return nil, fmt.Errorf(`unauthorized`)
|
return nil, fmt.Errorf(`unauthorized`)
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CreateVerificationToken creates a verification JWT token
|
// CreateVerificationToken creates a verification JWT token
|
||||||
func CreateVerificationToken(email, tokenType, hostname, nonceHash string) (string, error) {
|
func CreateVerificationToken(email, tokenType, hostname, nonceHash, redirectURL string) (string, error) {
|
||||||
claims := jwt.MapClaims{
|
claims := jwt.MapClaims{
|
||||||
"iss": hostname,
|
"iss": hostname,
|
||||||
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||||
@@ -18,7 +18,7 @@ func CreateVerificationToken(email, tokenType, hostname, nonceHash string) (stri
|
|||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": tokenType,
|
"token_type": tokenType,
|
||||||
"nonce": nonceHash,
|
"nonce": nonceHash,
|
||||||
"redirect_url": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL),
|
"redirect_uri": redirectURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
return SignJWTToken(claims)
|
return SignJWTToken(claims)
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>{{.data.organizationName}}</title>
|
<title>{{.data.organizationName}}</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/app/favicon_io/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/app/favicon_io/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/app/favicon_io/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/app/favicon_io/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/app/favicon_io/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/app/favicon_io/favicon-16x16.png">
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
(function (window, document) {
|
(function (window, document) {
|
||||||
var targetOrigin = {{.target_origin}};
|
var targetOrigin = {{.target_origin}};
|
||||||
var authorizationResponse = {{.authorization_response}};
|
var authorizationResponse = {{.authorization_response}};
|
||||||
console.log({targetOrigin})
|
|
||||||
window.parent.postMessage(authorizationResponse, targetOrigin);
|
window.parent.postMessage(authorizationResponse, targetOrigin);
|
||||||
})(this, this.document);
|
})(this, this.document);
|
||||||
</script>
|
</script>
|
||||||
|
Reference in New Issue
Block a user