Skip to content

Commit

Permalink
use auth0
Browse files Browse the repository at this point in the history
  • Loading branch information
argaen committed Feb 6, 2024
1 parent 205b164 commit 1b92a01
Show file tree
Hide file tree
Showing 22 changed files with 168 additions and 685 deletions.
1 change: 0 additions & 1 deletion amplify/backend/function/stockerlambda/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"body-parser": "^1.17.1",
"cors": "^2.8.5",
"express": "^4.17.3",
"googleapis": "^131.0.0",
"luxon": "^3.3.4"
},
"scripts": {
Expand Down
44 changes: 0 additions & 44 deletions amplify/backend/function/stockerlambda/ts/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import cors from 'cors';
import express from 'express';
import bodyParser from 'body-parser';
import awsServerlessExpressMiddleware from 'aws-serverless-express/middleware';
import { Auth } from 'googleapis';

import * as yahoo from './yahoo';
import { Price } from './types';
Expand Down Expand Up @@ -31,49 +30,6 @@ app.use(cors({
}
}));

app.get('/user/authorize', async (req, res) => {
const code = req.query.code as string;
if (!code) {
res.status(400).json({
error: 'CODE_REQUIRED',
description: 'Code is required for this endpoint',
});
}

const oauth2Client = new Auth.OAuth2Client(
'123339406534-gnk10bh5hqo87qlla8e9gmol1j961rtg.apps.googleusercontent.com',
process.env.CLIENT_SECRET,
'postmessage'
);

const response = await oauth2Client.getToken(code);

res.json(response.tokens);
});

app.get('/user/refresh', async (req, res) => {
const refresh_token = req.query.refresh_token as string;
if (!refresh_token) {
res.status(400).json({
error: 'REFRESH_TOKEN_REQUIRED',
description: 'Refresh token is required for this endpoint',
});
}

const oauth2Client = new Auth.OAuth2Client(
'123339406534-gnk10bh5hqo87qlla8e9gmol1j961rtg.apps.googleusercontent.com',
process.env.CLIENT_SECRET,
'postmessage'
);

oauth2Client.setCredentials({
refresh_token,
});
const response = await oauth2Client.refreshAccessToken();

res.json(response.credentials);
});

app.get('/api/prices', async (req, res) => {
let tickers = (req.query.ids as string || '').split(',');
if (!tickers.length) {
Expand Down
4 changes: 2 additions & 2 deletions jest.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ const config = {
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
coverageThreshold: {
global: {
lines: 92.9,
branches: 86.6,
lines: 92.6,
branches: 86,
},
},
testEnvironment: 'jest-environment-jsdom',
Expand Down
108 changes: 31 additions & 77 deletions src/__tests__/app/user/login/page.test.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import React from 'react';
import { act, render, screen } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import * as navigation from 'next/navigation';
import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';
import * as auth0 from '@auth0/auth0-react';

import * as Stocker from '@/lib/Stocker';
import LoginPage from '@/app/user/login/page';
import * as helpers_env from '@/helpers/env';
import * as sessionHook from '@/hooks/useSession';
import type { Credentials } from '@/types/user';

jest.mock('swr');

jest.mock('next/navigation');

jest.mock('@auth0/auth0-react', () => ({
__esModule: true,
...jest.requireActual('@auth0/auth0-react'),
}));

jest.mock('@/lib/Stocker', () => ({
__esModule: true,
...jest.requireActual('@/lib/Stocker'),
Expand All @@ -23,101 +26,52 @@ jest.mock('@/helpers/env', () => ({
isStaging: () => false,
}));

jest.mock('@/hooks/useSession', () => ({
__esModule: true,
...jest.requireActual('@/hooks/useSession'),
}));

describe('LoginPage', () => {
let requestCode: jest.Mock;
let mockInitCodeClient: jest.Mock;
let mockSetCredentials: jest.Mock<React.Dispatch<React.SetStateAction<Credentials>>>;
let mockRouterPush: jest.Mock;

beforeEach(() => {
requestCode = jest.fn();
mockInitCodeClient = jest.fn().mockReturnValue({
requestCode,
}) as jest.Mock<typeof window.google.accounts.oauth2.initCodeClient>;
window.google = {
accounts: {
// @ts-ignore
oauth2: {
initCodeClient: mockInitCodeClient,
} as typeof window.google.accounts.oauth2,
} as typeof window.google.accounts,
};
mockRouterPush = jest.fn();
jest.spyOn(navigation, 'useRouter').mockImplementation(() => ({
push: mockRouterPush as AppRouterInstance['push'],
} as AppRouterInstance));

mockSetCredentials = jest.fn();
jest.spyOn(sessionHook, 'default').mockReturnValue({
session: undefined,
setCredentials: mockSetCredentials as React.Dispatch<
React.SetStateAction<Credentials | undefined> >,
} as sessionHook.SessionReturn);
jest.spyOn(helpers_env, 'isStaging').mockReturnValue(false);
jest.spyOn(auth0, 'useAuth0').mockReturnValue({
isAuthenticated: false,
} as auth0.Auth0ContextInterface<auth0.User>);
});

it('shows loading... when not finished', () => {
const { container } = render(<LoginPage />);
expect(container).toMatchSnapshot();
});

it('calls requestCode when clicking sign in button', async () => {
it('sends to dashboard when authenticated', async () => {
jest.spyOn(auth0, 'useAuth0').mockReturnValue({
isAuthenticated: true,
} as auth0.Auth0ContextInterface<auth0.User>);
render(<LoginPage />);

expect(mockInitCodeClient).toHaveBeenCalledWith({
callback: expect.any(Function),
client_id: '123339406534-gnk10bh5hqo87qlla8e9gmol1j961rtg.apps.googleusercontent.com',
scope: 'https://www.googleapis.com/auth/drive.file',
ux_mode: 'popup',
});

expect(requestCode).toBeCalledTimes(0);
screen.getByText('Sign In').click();
expect(requestCode).toBeCalledTimes(1);
expect(mockRouterPush).toHaveBeenCalledWith('/dashboard/accounts');
});

it('callback authorizes, saves session to storage and navigates to accounts page', async () => {
const credentials = {
access_token: 'access_token',
id_token: 'id_token',
refresh_token: 'refresh_token',
expiry_date: 123,
scope: '',
token_type: '',
};
jest.spyOn(Stocker, 'authorize').mockResolvedValue(credentials);

it('sends to dashboard when staging', async () => {
jest.spyOn(helpers_env, 'isStaging').mockReturnValue(true);
render(<LoginPage />);

const { callback } = mockInitCodeClient.mock.calls[0][0];
await act(async () => callback({ code: 'CODE' }));

expect(Stocker.authorize).toBeCalledWith('CODE');
expect(mockSetCredentials).toHaveBeenNthCalledWith(
1,
credentials,
);
expect(mockRouterPush).toHaveBeenCalledWith('/dashboard/accounts');
});

it('does not call requestCode when clicking sign in button and sends to /home/dashboard when staging', async () => {
jest.spyOn(helpers_env, 'isStaging').mockReturnValue(true);
render(<LoginPage />);
it('shows loading... when not finished', () => {
const { container } = render(<LoginPage />);
expect(container).toMatchSnapshot();
});

expect(mockInitCodeClient).toHaveBeenCalledWith({
callback: expect.any(Function),
client_id: '123339406534-gnk10bh5hqo87qlla8e9gmol1j961rtg.apps.googleusercontent.com',
scope: 'https://www.googleapis.com/auth/drive.file',
ux_mode: 'popup',
});
it('calls loginWithPopup when clicking sign in button', async () => {
const mockLogin = jest.fn();
jest.spyOn(auth0, 'useAuth0').mockReturnValue({
isAuthenticated: true,
loginWithPopup: mockLogin as Function,
} as auth0.Auth0ContextInterface<auth0.User>);

render(<LoginPage />);
screen.getByText('Sign In').click();
expect(requestCode).toBeCalledTimes(0);
expect(mockRouterPush).toHaveBeenCalledWith('/dashboard/accounts');
process.env.NEXT_PUBLIC_ENV = '';

expect(mockLogin).toBeCalled();
});
});
24 changes: 14 additions & 10 deletions src/__tests__/app/user/logout/page.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import { render } from '@testing-library/react';
import type { LinkProps } from 'next/link';
import * as auth0 from '@auth0/auth0-react';

import LogoutPage from '@/app/user/logout/page';
import * as sessionHook from '@/hooks/useSession';

jest.mock('next/link', () => jest.fn(
(
Expand All @@ -13,29 +13,33 @@ jest.mock('next/link', () => jest.fn(
),
));

jest.mock('@/hooks/useSession', () => ({
jest.mock('@auth0/auth0-react', () => ({
__esModule: true,
...jest.requireActual('@/hooks/useSession'),
...jest.requireActual('@auth0/auth0-react'),
}));

describe('LogoutPage', () => {
let mockRevoke: jest.Mock;
let mockLogout: jest.Mock;

beforeEach(() => {
mockRevoke = jest.fn();
jest.spyOn(sessionHook, 'default').mockReturnValue({
revoke: mockRevoke as Function,
} as sessionHook.SessionReturn);
mockLogout = jest.fn();
jest.spyOn(auth0, 'useAuth0').mockReturnValue({
logout: mockLogout as Function,
} as auth0.Auth0ContextInterface<auth0.User>);
});

it('matches snapshot', () => {
const { container } = render(<LogoutPage />);
expect(container).toMatchSnapshot();
});

it('sets empty accessToken', () => {
it('calls logout', () => {
render(<LogoutPage />);

expect(mockRevoke).toBeCalled();
expect(mockLogout).toBeCalledWith({
logoutParams: {
returnTo: 'http://localhost/user/login',
},
});
});
});
6 changes: 3 additions & 3 deletions src/__tests__/components/ProfileDropdown.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { render } from '@testing-library/react';

import ProfileDropdown from '@/components/ProfileDropdown';
import * as sessionHook from '@/hooks/useSession';
import { User } from '@/types/user';
import type { User } from '@auth0/auth0-react';

jest.mock('@/hooks/useSession', () => ({
__esModule: true,
Expand All @@ -19,7 +19,7 @@ describe('ProfileDropdown', () => {
jest.spyOn(sessionHook, 'default').mockReturnValue({
user: {
name: '',
image: '',
picture: '',
email: '',
} as User,
} as sessionHook.SessionReturn);
Expand All @@ -34,7 +34,7 @@ describe('ProfileDropdown', () => {
jest.spyOn(sessionHook, 'default').mockReturnValue({
user: {
name: 'Maffin IO',
image: 'https://example.com',
picture: 'https://example.com',
email: '[email protected]',
} as User,
} as sessionHook.SessionReturn);
Expand Down
7 changes: 2 additions & 5 deletions src/__tests__/hooks/useGapiClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { act, renderHook } from '@testing-library/react';
import useGapiClient from '@/hooks/useGapiClient';
import * as sessionHook from '@/hooks/useSession';
import * as helpers_env from '@/helpers/env';
import type { Credentials } from '@/types/user';

jest.mock('@/hooks/useSession', () => ({
__esModule: true,
Expand All @@ -22,8 +21,7 @@ describe('useGapiClient', () => {

jest.spyOn(helpers_env, 'isStaging').mockReturnValue(false);
jest.spyOn(sessionHook, 'default').mockReturnValue({
session: undefined,
setCredentials: jest.fn() as Function,
accessToken: '',
} as sessionHook.SessionReturn);
});

Expand Down Expand Up @@ -118,8 +116,7 @@ describe('useGapiClient', () => {
it('returns true when script onload finished and session and loads needed libraries', async () => {
jest.spyOn(sessionHook, 'default').mockReturnValue(
{
session: { access_token: 'access_token' } as Credentials,
setCredentials: jest.fn() as Function,
accessToken: 'access_token',
} as sessionHook.SessionReturn,
);
const mockGapiClientLoad: jest.MockedFunction<typeof window.gapi.client.load> = jest.fn();
Expand Down
Loading

0 comments on commit 1b92a01

Please sign in to comment.