Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion __tests__/auth-provider.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useContext } from 'react';
import React, { PropsWithChildren, useContext } from 'react';
import { mocked } from 'ts-jest/utils';
import Auth0Context from '../src/auth0-context';
import { renderHook, act } from '@testing-library/react-hooks';
import { Auth0Client } from '@auth0/auth0-spa-js';
import pkg from '../package.json';
import { createWrapper } from './helpers';
import Auth0Provider from '../src/auth0-provider';

const clientMock = mocked(new Auth0Client({ client_id: '', domain: '' }));

Expand Down Expand Up @@ -384,4 +385,50 @@ describe('Auth0Provider', () => {
__raw: '__test_raw_token__',
});
});

describe('with a user-provided client instance', () => {
it('should handle redirect callback success and clear the url', async () => {
window.history.pushState(
{},
document.title,
'/?code=__test_code__&state=__test_state__'
);
expect(window.location.href).toBe(
'https://www.example.com/?code=__test_code__&state=__test_state__'
);
clientMock.handleRedirectCallback.mockResolvedValueOnce({
appState: undefined,
});

const wrapper = ({ children }: PropsWithChildren<{}>): JSX.Element => (
<Auth0Provider client={clientMock as unknown as Auth0Client}>
{children}
</Auth0Provider>
);
const { waitForNextUpdate } = renderHook(() => useContext(Auth0Context), {
wrapper,
});
await waitForNextUpdate();
expect(clientMock.handleRedirectCallback).toHaveBeenCalled();
expect(window.location.href).toBe('https://www.example.com/');
});
});

it('should provide a getAccessTokenSilently wrapping the method of the provided client', async () => {
clientMock.getTokenSilently.mockResolvedValue('token');
const wrapper = ({ children }: PropsWithChildren<{}>): JSX.Element => (
<Auth0Provider client={clientMock as unknown as Auth0Client}>
{children}
</Auth0Provider>
);
const { waitForNextUpdate, result } = renderHook(
() => useContext(Auth0Context),
{ wrapper }
);
await waitForNextUpdate();
expect(result.current.getAccessTokenSilently).toBeInstanceOf(Function);
const token = await result.current.getAccessTokenSilently();
expect(clientMock.getTokenSilently).toHaveBeenCalled();
expect(token).toBe('token');
});
});
5 changes: 5 additions & 0 deletions examples/cra-react-query/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SKIP_PREFLIGHT_CHECK=true
REACT_APP_DOMAIN=your-tenant.auth0.com
REACT_APP_CLIENT_ID=yourclientid
REACT_APP_AUDIENCE=https://api.example.com/users
REACT_APP_API_PORT=3001
26 changes: 26 additions & 0 deletions examples/cra-react-query/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Intentionally removing package lock because it has problems when using local file resolutions
package-lock.json
18 changes: 18 additions & 0 deletions examples/cra-react-query/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# React Router example

This is an example of using `@auth0/auth0-react` with `react-query`, passing in a statically-defined `Auth0Client` which is utilized by code external to React.

Follow the steps in [examples/README.md](../README.md) to setup an Auth0 application and API.

Add the file `./examples/cra-react-query/.env` with the `domain` and `clientId` of the application and `audience` (your API identifier)

```dotenv
REACT_APP_DOMAIN=your_domain
REACT_APP_CLIENT_ID=your_client_id
REACT_APP_AUDIENCE=your_audience
SKIP_PREFLIGHT_CHECK=true # To workaround issues with nesting create-react-app in another package
```

Run `npm start` to start the application at http://localhost:3000

Start the API using the instructions in [examples/users-api/README.md](../users-api/README.md)
43 changes: 43 additions & 0 deletions examples/cra-react-query/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "cra-react-query",
"version": "0.1.0",
"private": true,
"dependencies": {
"@auth0/auth0-react": "file:../..",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"@types/history": "^4.7.6",
"@types/jest": "^24.9.1",
"@types/node": "^12.12.43",
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",
"@types/react-router-dom": "^5.1.5",
"history": "^4.10.1",
"react": "file:../../node_modules/react",
"react-dom": "file:../../node_modules/react-dom",
"react-query": "^2.23.1",
"react-router-dom": "^5.2.0",
"react-scripts": "^3.4.1",
"typescript": "^3.7.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Binary file added examples/cra-react-query/public/favicon.ico
Binary file not shown.
24 changes: 24 additions & 0 deletions examples/cra-react-query/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta
name="description"
content="Web site created using create-react-app"
/>

<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk"
crossorigin="anonymous"
/>
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
5 changes: 5 additions & 0 deletions examples/cra-react-query/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.spinner-border {
top: 50%;
position: fixed;
margin-top: -1rem;
}
34 changes: 34 additions & 0 deletions examples/cra-react-query/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { useAuth0 } from '@auth0/auth0-react';
import { createBrowserHistory } from 'history';
import { Route, Router, Switch } from 'react-router-dom';
import './App.css';
import { ProtectedRoute } from './ProtectedRoute';
import { Nav } from './Nav';
import { Error } from './Error';
import { Loading } from './Loading';
import { Users } from './Users';

// Use `createHashHistory` to use hash routing
export const history = createBrowserHistory();

function App() {
const { isLoading, error } = useAuth0();

if (isLoading) {
return <Loading />;
}

return (
<Router history={history}>
<Nav />
{error && <Error message={error.message} />}
<Switch>
<Route path="/" exact />
<ProtectedRoute path="/users" component={Users} />
</Switch>
</Router>
);
}

export default App;
9 changes: 9 additions & 0 deletions examples/cra-react-query/src/Error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

export function Error({ message }: { message: string }) {
return (
<div className="alert alert-danger" role="alert">
Oops... {message}
</div>
);
}
11 changes: 11 additions & 0 deletions examples/cra-react-query/src/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

export function Loading() {
return (
<div className="text-center">
<div className="spinner-border" role="status">
<span className="sr-only">Loading...</span>
</div>
</div>
);
}
58 changes: 58 additions & 0 deletions examples/cra-react-query/src/Nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useEffect, useState } from 'react';
import { useHistory, Link } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react';

export function Nav() {
const { isAuthenticated, user, loginWithRedirect, logout } = useAuth0();
const history = useHistory();
const [pathname, setPathname] = useState(() => history.location.pathname);

useEffect(() => {
return history.listen(({ pathname }) => setPathname(pathname));
}, [history]);

return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<span className="navbar-brand">@auth0/auth0-react</span>
<div className="collapse navbar-collapse">
<div className="navbar-nav">
<Link
to="/"
className={`nav-item nav-link${pathname === '/' ? ' active' : ''}`}
>
Home
</Link>
<Link
to="/users"
className={`nav-item nav-link${
pathname === '/users' ? ' active' : ''
}`}
>
Users
</Link>
</div>
</div>

{isAuthenticated ? (
<div>
<span id="hello">Hello, {user.name}!</span>{' '}
<button
className="btn btn-outline-secondary"
id="logout"
onClick={() => logout({ returnTo: window.location.origin })}
>
logout
</button>
</div>
) : (
<button
className="btn btn-outline-success"
id="login"
onClick={() => loginWithRedirect()}
>
login
</button>
)}
</nav>
);
}
16 changes: 16 additions & 0 deletions examples/cra-react-query/src/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { withAuthenticationRequired } from '@auth0/auth0-react';
import { Route } from 'react-router-dom';

export const ProtectedRoute = ({
component,
...args
}: React.PropsWithChildren<any>) => (
<Route
component={withAuthenticationRequired(component, {
// If using a Hash Router, you need to pass the hash fragment as `returnTo`
// returnTo: () => window.location.hash.substr(1),
})}
{...args}
/>
);
48 changes: 48 additions & 0 deletions examples/cra-react-query/src/Users.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import { Loading } from './Loading';
import { Error } from './Error';
import { useQuery } from 'react-query';

type User = {
name: string;
email: string;
}

export function Users() {
const { isLoading, error, data: users = [] } = useQuery<User[], Error>([
'/users',
{
audience: process.env.REACT_APP_AUDIENCE,
scope: 'read:users',
}
]);

if (isLoading) {
return <Loading />;
}

if (error) {
return <Error message={error.message} />;
}

return (
<table className="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
{users!.map(
({ name, email }: { name: string; email: string }, i: number) => (
<tr key={i}>
<td>{name}</td>
<td>{email}</td>
</tr>
)
)}
</tbody>
</table>
);
}
Loading