Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDK-1693] Renames #21

Merged
merged 3 commits into from
May 28, 2020
Merged
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
24 changes: 12 additions & 12 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@

So that we can access the router `history` outside of the `Router` component you need to [create your own history object](https://github.com/ReactTraining/react-router/blob/master/FAQ.md#how-do-i-access-the-history-object-outside-of-components). We can reference this object from the `Auth0Provider`'s `onRedirectCallback`.

We can then use the `withLoginRequired` HOC (Higher Order Component) to create a `ProtectedRoute` component that redirects anonymous users to the login page, before returning them to the protected route:
We can then use the `withAuthenticationRequired` HOC (Higher Order Component) to create a `ProtectedRoute` component that redirects anonymous users to the login page, before returning them to the protected route:

```jsx
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import { Auth0Provider, withLoginRequired } from '@auth0/auth0-react';
import { Auth0Provider, withAuthenticationRequired } from '@auth0/auth0-react';
import { createBrowserHistory } from 'history';
import Profile from './Profile';

export const history = createBrowserHistory();

const ProtectedRoute = ({ component, ...args }) => (
<Route component={withLoginRequired(component)} {...args} />
<Route component={withAuthenticationRequired(component)} {...args} />
);

const onRedirectCallback = (appState) => {
Expand Down Expand Up @@ -80,12 +80,12 @@ export const wrapRootElement = ({ element }) => {
};
```

Create a page that you want to be protected, e.g. a profile page, and wrap it in the `withLoginRequired` HOC:
Create a page that you want to be protected, e.g. a profile page, and wrap it in the `withAuthenticationRequired` HOC:

```jsx
// src/pages/profile.js
import React from 'react';
import { useAuth0, withLoginRequired } from '@auth0/auth0-react';
import { useAuth0, withAuthenticationRequired } from '@auth0/auth0-react';

const Profile = () => {
const { user } = useAuth0();
Expand All @@ -97,8 +97,8 @@ const Profile = () => {
);
};

// Wrap the component in the withLoginRequired handler
export default withLoginRequired(Profile);
// Wrap the component in the withAuthenticationRequired handler
export default withAuthenticationRequired(Profile);
```

## 3. Protecting a route in a Next.js app (in SPA mode)
Expand Down Expand Up @@ -138,12 +138,12 @@ class MyApp extends App {
export default MyApp;
```

Create a page that you want to be protected, e.g. a profile page, and wrap it in the `withLoginRequired` HOC:
Create a page that you want to be protected, e.g. a profile page, and wrap it in the `withAuthenticationRequired` HOC:

```jsx
// pages/profile.js
import React from 'react';
import { useAuth0, withLoginRequired } from '@auth0/auth0-react';
import { useAuth0, withAuthenticationRequired } from '@auth0/auth0-react';

const Profile = () => {
const { user } = useAuth0();
Expand All @@ -155,8 +155,8 @@ const Profile = () => {
);
};

// Wrap the component in the withLoginRequired handler
export default withLoginRequired(Profile);
// Wrap the component in the withAuthenticationRequired handler
export default withAuthenticationRequired(Profile);
```

## 4. Create a `useApi` hook for accessing protected APIs with an access token.
Expand All @@ -181,7 +181,7 @@ export const useApi = (url, options = {}) => {
(async () => {
try {
const { audience, scope, ...fetchOptions } = options;
const accessToken = await getToken({ audience, scope });
const accessToken = await getAccessTokenSilently({ audience, scope });
const res = await fetch(url, {
...fetchOptions,
headers: {
Expand Down
32 changes: 21 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Auth0 SDK for React Applications.

- [Installation](#installation)
- [Getting Started](#getting-started)
- [Advanced Use Cases](#advanced-use-cases)
- [Other Use Cases](#other-use-cases)
- [Contributing](#contributing)
- [Support + Feedback](#support--feedback)
- [Vulnerability Reporting](#vulnerability-reporting)
Expand Down Expand Up @@ -59,15 +59,22 @@ ReactDOM.render(
);
```

Use the `useAuth0` hook in your components to access authentication state (`isLoading`, `isAuthenticated` and `user`) and authentication methods (`login` and `logout`):
Use the `useAuth0` hook in your components to access authentication state (`isLoading`, `isAuthenticated` and `user`) and authentication methods (`loginWithRedirect` and `logout`):

```jsx
// src/App.js
import React from 'react';
import { useAuth0 } from '@auth0/auth0-react';

function App() {
const { isLoading, isAuthenticated, error, user, login, logout } = useAuth0();
const {
isLoading,
isAuthenticated,
error,
user,
loginWithRedirect,
logout,
} = useAuth0();

if (isLoading) {
return <div>Loading...</div>;
Expand All @@ -83,14 +90,14 @@ function App() {
</div>
);
} else {
return <button onClick={login}>Log in</button>;
return <button onClick={loginWithRedirect}>Log in</button>;
}
}

export default App;
```

## Advanced Use Cases
## Other Use Cases

### Class Components

Expand All @@ -112,18 +119,18 @@ export default withAuth0(Profile);

### Protecting Routes

Protect a route component using the `withLoginRequired` higher order component. Visits to this route when unauthenticated will redirect the user to the login page and back to this page after login:
Protect a route component using the `withAuthenticationRequired` higher order component. Visits to this route when unauthenticated will redirect the user to the login page and back to this page after login:

```jsx
import React from 'react';
import { withLoginRequired } from '@auth0/auth0-react';
import { withAuthenticationRequired } from '@auth0/auth0-react';

// Show a message while the user waits to be redirected to the login page.
const Redirecting = () => <div>Redirecting you to the login page...</div>;

const PrivateRoute = () => <div>Private</div>;

export default withLoginRequired(PrivateRoute, Redirecting);
export default withAuthenticationRequired(PrivateRoute, Redirecting);
```

### Access an API
Expand All @@ -135,20 +142,23 @@ import React, { useEffect, useState } from 'react';
import { useAuth0 } from '@auth0/auth0-react';

const Posts = () => {
const { getToken } = useAuth0();
const { getAccessTokenSilently } = useAuth0();
const [posts, setPosts] = useState(null);

useEffect(() => {
(async () => {
const token = await getToken();
const token = await getAccessTokenSilently({
audience: 'https://api.example.com/',
scope: 'read:posts',
});
const response = await fetch('https://api.example.com/posts', {
headers: {
Authorization: `Bearer ${token}`,
},
});
setPosts(await response.json());
})();
}, [getToken]);
}, [getAccessTokenSilently]);

if (!posts) {
return <div>Loading...</div>;
Expand Down
18 changes: 10 additions & 8 deletions __tests__/auth-provider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,10 @@ describe('Auth0Provider', () => {
{ wrapper }
);
await waitForNextUpdate();
expect(result.current.login).toBeInstanceOf(Function);
await result.current.login({ redirect_uri: '__redirect_uri__' });
expect(result.current.loginWithRedirect).toBeInstanceOf(Function);
await result.current.loginWithRedirect({
redirect_uri: '__redirect_uri__',
});
expect(clientMock.loginWithRedirect).toHaveBeenCalledWith({
redirect_uri: '__redirect_uri__',
});
Expand All @@ -217,30 +219,30 @@ describe('Auth0Provider', () => {
});
});

it('should provide a getToken method', async () => {
it('should provide a getAccessTokenSilently method', async () => {
clientMock.getTokenSilently.mockResolvedValue('token');
const wrapper = createWrapper();
const { waitForNextUpdate, result } = renderHook(
() => useContext(Auth0Context),
{ wrapper }
);
await waitForNextUpdate();
expect(result.current.getToken).toBeInstanceOf(Function);
const token = await result.current.getToken();
expect(result.current.getAccessTokenSilently).toBeInstanceOf(Function);
const token = await result.current.getAccessTokenSilently();
expect(clientMock.getTokenSilently).toHaveBeenCalled();
expect(token).toBe('token');
});

it('should provide a getTokenWithPopup method', async () => {
it('should provide a getAccessTokenWithPopup method', async () => {
clientMock.getTokenWithPopup.mockResolvedValue('token');
const wrapper = createWrapper();
const { waitForNextUpdate, result } = renderHook(
() => useContext(Auth0Context),
{ wrapper }
);
await waitForNextUpdate();
expect(result.current.getTokenWithPopup).toBeInstanceOf(Function);
const token = await result.current.getTokenWithPopup();
expect(result.current.getAccessTokenWithPopup).toBeInstanceOf(Function);
const token = await result.current.getAccessTokenWithPopup();
expect(clientMock.getTokenWithPopup).toHaveBeenCalled();
expect(token).toBe('token');
});
Expand Down
6 changes: 4 additions & 2 deletions __tests__/ssr.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ describe('In a Node SSR environment', () => {
const wrapper = createWrapper();
const {
result: {
current: { isLoading, isAuthenticated, user, login },
current: { isLoading, isAuthenticated, user, loginWithRedirect },
},
} = renderHook(useAuth0, { wrapper });
expect(isLoading).toBeFalsy();
expect(isAuthenticated).toBeFalsy();
expect(user).toBeUndefined();
await expect(login).rejects.toThrowError('window is not defined');
await expect(loginWithRedirect).rejects.toThrowError(
'window is not defined'
);
});
});
2 changes: 1 addition & 1 deletion __tests__/use-auth.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('useAuth0', () => {
const {
result: { current },
} = renderHook(useAuth0);
expect(current.login).toThrowError(
expect(current.loginWithRedirect).toThrowError(
'You forgot to wrap your component in <Auth0Provider>.'
);
});
Expand Down
13 changes: 8 additions & 5 deletions __tests__/withLoginRequired.test.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import withLoginRequired from '../src/with-login-required';
import withAuthenticationRequired from '../src/with-login-required';
import { render, screen, waitFor } from '@testing-library/react';
import { Auth0Client } from '@auth0/auth0-spa-js';
import Auth0Provider from '../src/auth0-provider';
import { mocked } from 'ts-jest';

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

describe('withLoginRequired', () => {
describe('withAuthenticationRequired', () => {
it('should block access to a private component when not authenticated', async () => {
const MyComponent = (): JSX.Element => <>Private</>;
const WrappedComponent = withLoginRequired(MyComponent);
const WrappedComponent = withAuthenticationRequired(MyComponent);
render(
<Auth0Provider client_id="__test_client_id__" domain="__test_domain__">
<WrappedComponent />
Expand All @@ -26,7 +26,7 @@ describe('withLoginRequired', () => {
it('should allow access to a private component when authenticated', async () => {
mockClient.isAuthenticated.mockResolvedValue(true);
const MyComponent = (): JSX.Element => <>Private</>;
const WrappedComponent = withLoginRequired(MyComponent);
const WrappedComponent = withAuthenticationRequired(MyComponent);
render(
<Auth0Provider client_id="__test_client_id__" domain="__test_domain__">
<WrappedComponent />
Expand All @@ -42,7 +42,10 @@ describe('withLoginRequired', () => {
mockClient.isAuthenticated.mockResolvedValue(true);
const MyComponent = (): JSX.Element => <>Private</>;
const OnRedirecting = (): JSX.Element => <>Redirecting</>;
const WrappedComponent = withLoginRequired(MyComponent, OnRedirecting);
const WrappedComponent = withAuthenticationRequired(
MyComponent,
OnRedirecting
);
render(
<Auth0Provider client_id="__test_client_id__" domain="__test_domain__">
<WrappedComponent />
Expand Down
16 changes: 10 additions & 6 deletions src/auth0-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ export interface Auth0ContextInterface extends AuthState {
/**
* Get an access token.
*/
getToken: (options?: GetTokenSilentlyOptions) => Promise<string>;
getAccessTokenSilently: (
options?: GetTokenSilentlyOptions
) => Promise<string>;

/**
* Get an access token interactively.
*/
getTokenWithPopup: (options?: GetTokenWithPopupOptions) => Promise<string>;
getAccessTokenWithPopup: (
options?: GetTokenWithPopupOptions
) => Promise<string>;

/**
* Returns all claims from the id_token if available.
Expand All @@ -29,7 +33,7 @@ export interface Auth0ContextInterface extends AuthState {
/**
* Login in with a redirect.
*/
login: (options?: RedirectLoginOptions) => Promise<void>;
loginWithRedirect: (options?: RedirectLoginOptions) => Promise<void>;

/**
* Login in with a popup.
Expand All @@ -48,10 +52,10 @@ const stub = (): never => {

export default createContext<Auth0ContextInterface>({
...initialAuthState,
getToken: stub,
getTokenWithPopup: stub,
getAccessTokenSilently: stub,
getAccessTokenWithPopup: stub,
getIdTokenClaims: stub,
login: stub,
loginWithRedirect: stub,
loginWithPopup: stub,
logout: stub,
});
8 changes: 5 additions & 3 deletions src/auth0-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ const Auth0Provider = ({
<Auth0Context.Provider
value={{
...state,
getToken: (opts): Promise<string> => client.getTokenSilently(opts),
getTokenWithPopup: (opts): Promise<string> =>
getAccessTokenSilently: (opts): Promise<string> =>
client.getTokenSilently(opts),
getAccessTokenWithPopup: (opts): Promise<string> =>
client.getTokenWithPopup(opts),
getIdTokenClaims: (opts): Promise<IdToken> =>
client.getIdTokenClaims(opts),
login: (opts): Promise<void> => client.loginWithRedirect(opts),
loginWithRedirect: (opts): Promise<void> =>
client.loginWithRedirect(opts),
loginWithPopup: (opts): Promise<void> => loginWithPopup(opts),
logout: (opts): void => client.logout(opts),
}}
Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ export {
} from './auth0-provider';
export { default as useAuth0 } from './use-auth0';
export { default as withAuth0, WithAuth0Props } from './with-auth0';
export { default as withLoginRequired } from './with-login-required';
export { default as withAuthenticationRequired } from './with-login-required';
export { Auth0ContextInterface } from './auth0-context';
10 changes: 5 additions & 5 deletions src/with-login-required.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ import useAuth0 from './use-auth0';

export const defaultOnRedirecting = (): JSX.Element => <></>;

const withLoginRequired = <P extends object>(
const withAuthenticationRequired = <P extends object>(
Component: ComponentType<P>,
onRedirecting: () => JSX.Element = defaultOnRedirecting
): FC<P> => (props: P): JSX.Element => {
const { isAuthenticated, isLoading, login } = useAuth0();
const { isAuthenticated, isLoading, loginWithRedirect } = useAuth0();

useEffect(() => {
if (isLoading || isAuthenticated) {
return;
}
(async (): Promise<void> => {
await login({
await loginWithRedirect({
appState: { returnTo: window.location.pathname },
});
})();
}, [isLoading, isAuthenticated, login]);
}, [isLoading, isAuthenticated, loginWithRedirect]);

return isAuthenticated ? <Component {...props} /> : onRedirecting();
};

export default withLoginRequired;
export default withAuthenticationRequired;
Loading