Compare commits

...

4 Commits

Author SHA1 Message Date
Lakhan Samani
2bf6b8f91d fix: remove log 2022-03-09 17:24:53 +05:30
Lakhan Samani
776c0fba8b chore: app dependencies 2022-03-09 17:21:55 +05:30
Lakhan Samani
dd64aa2e79 feat: add version info 2022-03-09 11:53:34 +05:30
Lakhan Samani
157b13baa7 fix: basic auth redirect 2022-03-09 10:10:39 +05:30
16 changed files with 132 additions and 37 deletions

30
app/package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "0.9.0-beta.3", "@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.4.0-beta.0", "version": "0.4.0-beta.3",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.0.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.3.tgz",
"integrity": "sha512-wNh5ROldNqdbOXFPDlq1tObzPZyEQkbnOvSEwvnDfPYb9/BsJ3naj3/ayz4J2R5k2+Eyuk0LK64XYdkfIW0HYA==", "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.9.0-beta.3", "version": "0.9.0-beta.7",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.3.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.7.tgz",
"integrity": "sha512-P93PW6W3Qm9BW3160gn0Ce+64UCFAOpoEOHf5537LgFPE8LpNAIU3EI6EtMNkOJS58pu1h2UkfyRyX/j0Pohjw==", "integrity": "sha512-hCGsVionKMZNk+uD0CLtMIkUzhQqpHbVntko3rY+O7ouOrTrikY/WQVPbo1bqX1cu/6/cHE4RVU3cZ7V5xnxVg==",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-js": "^0.4.0-beta.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.4.0-beta.0", "version": "0.4.0-beta.3",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.0.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.4.0-beta.3.tgz",
"integrity": "sha512-wNh5ROldNqdbOXFPDlq1tObzPZyEQkbnOvSEwvnDfPYb9/BsJ3naj3/ayz4J2R5k2+Eyuk0LK64XYdkfIW0HYA==", "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.9.0-beta.3", "version": "0.9.0-beta.7",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.3.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.9.0-beta.7.tgz",
"integrity": "sha512-P93PW6W3Qm9BW3160gn0Ce+64UCFAOpoEOHf5537LgFPE8LpNAIU3EI6EtMNkOJS58pu1h2UkfyRyX/j0Pohjw==", "integrity": "sha512-hCGsVionKMZNk+uD0CLtMIkUzhQqpHbVntko3rY+O7ouOrTrikY/WQVPbo1bqX1cu/6/cHE4RVU3cZ7V5xnxVg==",
"requires": { "requires": {
"@authorizerdev/authorizer-js": "^0.4.0-beta.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"

View File

@@ -11,7 +11,7 @@
"author": "Lakhan Samani", "author": "Lakhan Samani",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "0.9.0-beta.3", "@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",

View File

@@ -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() {
const searchParams = new URLSearchParams(window.location.search);
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 // @ts-ignore
const globalState: Record<string, string> = window['__authorizer__']; ...window['__authorizer__'],
...urlProps,
};
return ( return (
<div <div
style={{ style={{
@@ -38,7 +61,7 @@ export default function App() {
redirectURL: globalState.redirectURL, redirectURL: globalState.redirectURL,
}} }}
> >
<Root /> <Root globalState={globalState} />
</AuthorizerProvider> </AuthorizerProvider>
</BrowserRouter> </BrowserRouter>
</div> </div>

View File

@@ -6,14 +6,20 @@ 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) {
console.log({ token });
let redirectURL = config.redirectURL || '/app'; let redirectURL = config.redirectURL || '/app';
const params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&refresh_token=${token.refresh_token}`; 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); const url = new URL(redirectURL);
if (redirectURL.includes('?')) { if (redirectURL.includes('?')) {
redirectURL = `${redirectURL}&${params}`; redirectURL = `${redirectURL}&${params}`;

22
app/src/utils/common.ts Normal file
View 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('&');
};

View File

@@ -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>
); );
}; };

View File

@@ -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{

View File

@@ -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>
{fetching ? (
<Spinner />
) : (
<>
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl"> <Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
{children} {children}
</Box> </Box>
<Text color="gray.600" fontSize="sm">
Current Version: {data.meta.version}
</Text>
</>
)}
</Flex> </Flex>
); );
} }

View File

@@ -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';

View File

@@ -1,7 +1,7 @@
package email package email
import ( import (
"fmt" "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"
@@ -107,7 +107,7 @@ func SendVerificationMail(toEmail, token, hostname string) error {
err := SendMail(Receiver, Subject, message) err := SendMail(Receiver, Subject, message)
if err != nil { if err != nil {
fmt.Println("=> error sending email:", err) log.Println("=> error sending email:", err)
} }
return err return err
} }

View File

@@ -1343,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 {
@@ -7298,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
}
} }
} }

View File

@@ -153,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 {

View File

@@ -181,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 {

View File

@@ -109,7 +109,5 @@ func (r *Resolver) Mutation() generated.MutationResolver { return &mutationResol
// Query returns generated.QueryResolver implementation. // Query returns generated.QueryResolver implementation.
func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }
type ( type mutationResolver struct{ *Resolver }
mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver }
queryResolver struct{ *Resolver }
)

View File

@@ -151,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 {

View File

@@ -2,7 +2,6 @@ package token
import ( import (
"errors" "errors"
"fmt"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/crypto" "github.com/authorizerdev/authorizer/server/crypto"
@@ -92,7 +91,6 @@ func ParseJWTToken(token, hostname, nonce, subject string) (jwt.MapClaims, error
return claims, errors.New("invalid audience") return claims, errors.New("invalid audience")
} }
fmt.Println("claims:", claims["nonce"], nonce, claims["nonce"] == nonce)
if claims["nonce"] != nonce { if claims["nonce"] != nonce {
return claims, errors.New("invalid nonce") return claims, errors.New("invalid nonce")
} }