Skip to content

Commit

Permalink
Merge pull request #625 from etn-ccis/feature/update-docs-for-okta-login
Browse files Browse the repository at this point in the history
Feature/update docs for okta login
  • Loading branch information
shubham-eaton authored Aug 28, 2024
2 parents e44f36f + a526674 commit 9a43f97
Show file tree
Hide file tree
Showing 24 changed files with 402 additions and 112 deletions.
2 changes: 1 addition & 1 deletion login-workflow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The React Auth Workflow package provides a consistent UI implementation of authe

The package is intended to provide a standard, out-of-the-box experience for capabilities such as:

- Okta Login
- Okta Login (Recommended)
- Login
- Forgot / Reset Password
- Change Password
Expand Down
2 changes: 1 addition & 1 deletion login-workflow/docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Exported screens that can be used to customize the auth workflow.
- [EulaScreen](./screens/eula.md)
- [ForgotPasswordScreen](./screens/forgot-password.md)
- [LoginScreen](./screens/login.md)
- [OktaLoginScreen](./screens/okta-login.md)
- [OktaRedirectLoginScreen](./screens/okta-login.md)
- [ResetPasswordScreen](./screens/reset-password.md)
- [SuccessScreen](./screens/success.md)
- [VerifyCodeScreen](./screens/verify-code.md)
71 changes: 64 additions & 7 deletions login-workflow/docs/authentication-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,65 @@

The authentication workflow includes screens related to user authentication including Login, Forgot Password, Reset Password, etc.

This guide covers two approaches for implementing login/authentication workflows: Okta and Custom Login.

## AuthContextProvider

## Okta Approach
The Okta approach leverages Okta's identity and access management services to handle user authentication, including login and password management. By integrating with Okta, you can offload the complexities of authentication and focus on building your application.

### OktaAuthContextProvider

The login screen in this workflow accesses shared data / configuration / API definitions through an `OktaAuthContextProvider` which should wrap the screen. We recommend using Okta to manage the additional password-related screens (Forgot / Reset / Change), but if you prefer to use those screens from this workflow, you'll need to wrap those screens with the `AuthContextProvider` (see custom workflow below).

You must supply the `OktaAuthContextProvider` with the following props / data:
- `language`: configures the language displayed on the screens
- `navigate`: a function that can be called to navigate to a new route
- `routeConfig`: an object describing the URLs you are using for the relevant routes so the workflow can correctly navigate between screens

More information about the required and optional props can found in the [API](#okta-auth-context-provider-api) section.

### Example

Here is an example of how you would set up the Okta Redirect Login workflow using our recommended routing solution ([React Router](https://reactrouter.com/)).

Each feature/screen from the Okta Auth Workflow that you wish to use should be rendered on a separate route.

```tsx
<Routes>
<Route
element={
<OktaAuthContextProvider
language={'en'}
navigate={navigate}
routeConfig={{}}
>
<OktaRedirectLogin />
</OktaAuthContextProvider>
}
/>
</Routes>
```

For a detailed explanation of setting up routes, see the [Routing](./routing.md) guide.

### Okta Auth Context Provider API

### OktaAuthContextProviderProps

| Prop Name | Type | Description | Default |
|---|---|---|---|
| language* | `string` | The language code specifying which language to use for the UI | `'en'` |
| navigate* | `(url: string) => void` | A function that is used to navigate to a new URL. This is used to navigate to the various screens of the authentication workflow. | |
| routeConfig* | `RouteConfig` | An object that defines the various routes for the authentication workflow. See [RouteConfig](#routeconfig) for more information. | |
| i18n | `i18n` | An optional i18n object that is used to translate the UI. This is only needed if you want to use custom translation keys / languages inside any of the workflow screens | |
| errorConfig | `ErrorContextProviderProps` | An object that is used to configure error handling within the workflow. See [Error Management](./error-management.md) for more information. | |


## Custom Login Approach

The Custom Login approach allows you to implement your own authentication logic. This workflow includes screens related to user authentication such as Login, Forgot Password, Reset Password, etc. By implementing your own authentication logic, you have full control over the authentication process and can customize it to meet your application's needs.

### AuthContextProvider

The screens in this workflow access shared data / configuration / API definitions through an `AuthContextProvider` which should wrap all of the relevant routes / screens.

Expand All @@ -15,7 +72,7 @@ You must supply the `AuthContextProvider` with the following props / data:

More information about the required and optional props can found in the [API](#api) section.

## Implement AuthUIActions
### Implement AuthUIActions

Because this workflow package is back-end agnostic, you must provide an implementation for what happens when the user triggers certain behaviors in the UI.

Expand All @@ -27,7 +84,7 @@ The example project includes a skeleton implementation of all required functions
2. You might also want to copy over the `example/src/store` and `example/src/constants` folders, which provide a very basic mechanism for storing important data using LocalStorage
- You will want to switch this out for a more secure approach before going to production with your application.

## Example
### Example

Here is an example of how you would set up the Login workflow using our recommended routing solution ([React Router](https://reactrouter.com/)).

Expand Down Expand Up @@ -61,9 +118,9 @@ Each feature/screen from the Auth Workflow that you wish to use should be render

For a detailed explanation of setting up routes, see the [Routing](./routing.md) guide.

## API
### Auth Context Provider API

### AuthContextProviderProps
#### AuthContextProviderProps

| Prop Name | Type | Description | Default |
|---|---|---|---|
Expand All @@ -76,7 +133,7 @@ For a detailed explanation of setting up routes, see the [Routing](./routing.md)
| errorConfig | `ErrorContextProviderProps` | An object that is used to configure error handling within the workflow. See [Error Management](./error-management.md) for more information. | |


### AuthUIActions
#### AuthUIActions

| Prop Name | Type | Description | Default |
|---|---|---|---|
Expand All @@ -87,7 +144,7 @@ For a detailed explanation of setting up routes, see the [Routing](./routing.md)
| setPassword | `(code: string, password: string, email?: string) => Promise<void>` | A function that is used to set a new password. This function will be called when the user clicks the Next button on the Reset Password screen. | |
| changePassword | `(oldPassword: string, newPassword: string) => Promise<void>` | A function that is used to change a user's password. This function will be called when the user clicks the Next button in the Change Password dialog. | |

### RouteConfig Object
#### RouteConfig Object

The RouteConfig is an object that specifies the paths you are using for the routes / screens in your application to facilitate navigating between screens within the workflows.

Expand Down
79 changes: 76 additions & 3 deletions login-workflow/docs/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,80 @@ Because this workflow package is router-agnostic, you will be required to set up

You will also want to set up Auth/Guest Guard wrappers to control which users can access which screens / routes. For more information see [Protecting Routes](#protecting-routes) below.

### Authentication
### Authentication (Okta Redirect)

The **Authentication** workflow screens are rendered individually on separate routes (e.g., the Okta Redirect Login screen is on '/login'). This means you can deep-link to any of these screens directly if you have them configured.

You have to add a `Security` component as a wrapper to the `Routes` from [@okta/okta-react](https://www.npmjs.com/package/@okta/okta-react) package. This component initializes the Okta authentication context and provides methods to interact with Okta.

For more information on the `OktaAuthContextProvider`, refer to the [Authentication Workflow](./authentication-workflow.md#OktaAuthContextProvider) Guide.

#### Example Setup

```tsx
import React from 'react';
import {
AuthContextProvider,
ContactSupportScreen,
ForgotPasswordScreen,
ResetPasswordScreen,
OktaRedirectLoginScreen,
ReactRouterGuestGuard,
ContactSupportScreen,
} from '@brightlayer-ui/react-auth-workflow';
import { useNavigate } from 'react-router';
import { ProjectAuthUIActions } from '../actions/AuthUIActions';
import { Outlet, Route, Routes } from 'react-router-dom';
import { Security } from '@okta/okta-react';
import OktaAuth, { OktaAuthOptions, toRelativeUrl } from '@okta/okta-auth-js';
import oktaConfig from '../oktaConfig';
import { OktaLogin } from '../screens/OktaLogin';

export const routes: RouteConfig = {
LOGIN: '/login',
REGISTER_INVITE: '/register-by-invite?code=8k27jshInvite234Code&[email protected]',
REGISTER_SELF: '/self-registration',
FORGOT_PASSWORD: '/forgot-password',
RESET_PASSWORD: '/reset-password',
SUPPORT: '/support',
};

const oktaAuth = new OktaAuth(oktaConfig as OktaAuthOptions);

export const AppRouter: React.FC = () => {
const navigation = useNavigate();
const navigate = useCallback((destination: -1 | string) => {
navigation(destination as To);
}, []);

const restoreOriginalUri = (_oktaAuth: any, originalUri: any): void => {
navigate(toRelativeUrl(originalUri || '/', window.location.origin));
};

return (
<Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri}>
<Routes>
<Route
path={routes.LOGIN}
element={
<ReactRouterGuestGuard isAuthenticated={appState.isAuthenticated} fallBackUrl={'/'}>
<OktaAuthContextProvider
language={'en'}
navigate={navigate}
routeConfig={routes}
>
<OktaRedirectLoginScreen />
</OktaAuthContextProvider>
</ReactRouterGuestGuard>
}
/>
</Routes>
</Security>
);
};
```

### Authentication for Custom Login

The **Authentication** workflow screens are rendered individually on separate routes (e.g., the Login screen is on '/login' and the support screen is on '/support'). This means you can deep-link to any of these screens directly if you have them configured.

Expand All @@ -23,7 +96,7 @@ import {
ContactSupportScreen,
ForgotPasswordScreen,
ResetPasswordScreen,
OktaLoginScreen,
LoginScreen,
ReactRouterGuestGuard,
ContactSupportScreen,
} from '@brightlayer-ui/react-auth-workflow';
Expand Down Expand Up @@ -69,7 +142,7 @@ export const AppRouter: React.FC = () => {
path={routes.LOGIN}
element={
<ReactRouterGuestGuard isAuthenticated={appState.isAuthenticated} fallBackUrl={'/'}>
<OktaLoginScreen />
<LoginScreen />
</ReactRouterGuestGuard>
}
/>
Expand Down
2 changes: 1 addition & 1 deletion login-workflow/docs/screens/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The following screen components are available:
- [EulaScreen](./eula.md)
- [ForgotPasswordScreen](./forgot-password.md)
- [LoginScreen](./login.md)
- [OktaLoginScreen](./okta-login.md)
- [OktaRedirectLoginScreen](./okta-login.md)
- [ResetPasswordScreen](./reset-password.md)
- [SuccessScreen](./success.md)
- [VerifyCodeScreen](./verify-code.md)
9 changes: 4 additions & 5 deletions login-workflow/docs/screens/okta-login.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# OktaLoginScreen
# OktaRedirectLoginScreen

The `OktaLoginScreen` component is a part of the login workflow and is used to render a login screen that integrates with Okta for authentication. The OktaLoginScreen must be used within an `AuthContextProvider`
The `OktaRedirectLoginScreen` component is a part of the login workflow and is used to render a login screen that integrates with Okta for authentication. The OktaRedirectLoginScreen must be used within an `OktaAuthContextProvider`

![Okta Login](../../media/screens/okta-login.png)

## Import

```tsx
import { OktaLoginScreen } from '@brightlayer-ui/react-auth-workflow';
import { OktaRedirectLoginScreen } from '@brightlayer-ui/react-auth-workflow';

...

<OktaLoginScreen />
<OktaRedirectLoginScreen />
```

## API
Expand All @@ -30,7 +30,6 @@ import { OktaLoginScreen } from '@brightlayer-ui/react-auth-workflow';
| showContactSupport | `boolean` | Whether or not to show the 'contact support' link. | `true` |
| contactSupportLabel | `string` | Label for the 'contact support' link. | `t('bluiCommon:MESSAGES.CONTACT')` |
| onContactSupport | `() => void` | Callback function that is called when the 'contact support' link is clicked. | |
| errorDisplayConfig | `ErrorManagerProps` | See [Error Management](../error-management.md) | |
| showCyberSecurityBadge | `boolean` | Whether or not to show the cyber security badge. | `true` |
| projectImage | `ReactNode` | Image to display at the top of the screen. | |
| header | `ReactNode` | Custom content to display at the top of the screen. | |
Expand Down
12 changes: 10 additions & 2 deletions login-workflow/example/src/navigation/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useCallback } from 'react';
import {
AuthContextProvider,
OktaAuthContextProvider,
ContactSupportScreen,
ReactRouterAuthGuard,
ReactRouterGuestGuard,
Expand All @@ -21,7 +22,7 @@ import { ChangePassword } from '../components/ChangePassword';
import { Security } from '@okta/okta-react';
import OktaAuth, { OktaAuthOptions, toRelativeUrl } from '@okta/okta-auth-js';
import oktaConfig from '../oktaConfig';
import { OktaLogin } from '../screens/OktaLogin';
import { OktaLogin } from '../screens/OktaRedirectLogin';

const oktaAuth = new OktaAuth(oktaConfig as OktaAuthOptions);

Expand Down Expand Up @@ -59,7 +60,14 @@ export const AppRouter: React.FC = () => {
path={'/login'}
element={
<ReactRouterGuestGuard isAuthenticated={app.isAuthenticated} fallBackUrl={'/'}>
<OktaLogin />
<OktaAuthContextProvider
language={app.language}
navigate={navigate}
routeConfig={routes}
i18n={i18nAppInstance}
>
<OktaLogin />
</OktaAuthContextProvider>
</ReactRouterGuestGuard>
}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import React from 'react';
import { OktaLoginScreen } from '@brightlayer-ui/react-auth-workflow';
import { OktaRedirectLoginScreen } from '@brightlayer-ui/react-auth-workflow';
import EatonLogo from '../assets/images/eaton_stacked_logo.png';
import { DebugComponent } from '../components/DebugComponent';

export const OktaLogin = (): JSX.Element => (
<OktaLoginScreen
<OktaRedirectLoginScreen
projectImage={<img src={EatonLogo} alt="logo" style={{ maxHeight: 80 }} />}
header={<DebugComponent />}
errorDisplayConfig={{
mode: 'message-box',
messageBoxConfig: {
dismissible: true,
position: 'top',
},
}}
/>
);
2 changes: 1 addition & 1 deletion login-workflow/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@brightlayer-ui/react-auth-workflow",
"version": "5.0.0-beta.0",
"version": "5.0.0-beta.1",
"author": "Brightlayer UI <[email protected]> (https://github.com/brightlayer-ui)",
"license": "BSD-3-Clause",
"description": "Re-usable workflow components for Authentication and Registration within Eaton applications.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import { render, cleanup, screen, renderHook } from '@testing-library/react';
import '@testing-library/jest-dom';
import { OktaAuthContextProvider } from './provider';
import { useOktaAuthContext } from '.';
import { authContextProviderProps } from '../../testUtils';

afterEach(cleanup);

describe('OktaAuthContextProvider', () => {
it('should render OktaAuthContextProvider without crashing', () => {
render(<OktaAuthContextProvider {...authContextProviderProps}>Hello Auth</OktaAuthContextProvider>);

expect(screen.getByText('Hello Auth')).toBeInTheDocument();
});

it('should read values from the context', () => {
const wrapper = ({ children }: any): JSX.Element => (
<OktaAuthContextProvider {...authContextProviderProps}>{children}</OktaAuthContextProvider>
);
const { result } = renderHook(() => useOktaAuthContext(), { wrapper });

expect(result.current.language).toBe('en');
});

it('should set values in the context', () => {
const wrapper = ({ children }: any): JSX.Element => (
<OktaAuthContextProvider {...authContextProviderProps} language="es">
{children}
</OktaAuthContextProvider>
);
const { result } = renderHook(() => useOktaAuthContext(), { wrapper });

expect(result.current.language).not.toBe('en');
expect(result.current.language).toBe('es');
});

it('should render multiple children', () => {
render(
<OktaAuthContextProvider {...authContextProviderProps}>
<div>Child 1</div>
<div>Child 2</div>
</OktaAuthContextProvider>
);

expect(screen.getByText('Child 1')).toBeInTheDocument();
expect(screen.getByText('Child 2')).toBeInTheDocument();
});
});
11 changes: 11 additions & 0 deletions login-workflow/src/contexts/OktaAuthContext/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @packageDocumentation
* @module OktaAuthContext
*/
import { createContext } from 'react';
import { OktaAuthContextProviderProps } from './types';

/**
* Okta Auth Context is used to access context in the okta authentication workflow
*/
export const OktaAuthContext = createContext<OktaAuthContextProviderProps | null>(null);
Loading

0 comments on commit 9a43f97

Please sign in to comment.