# @orchard/frontend-identity

frontend-identity provides a unified integration of Orchard front ends and auth0. It provides a React hook `useAuthFlow` that handles the authentication flow and decoding of the JWT.

# Usage
## `useAuthFlow`

```jsx
import React from 'react';
import LocalizationManager from 'frontend-localization';
import * as Sentry from '@sentry/browser';
import { useAuthFlow, IdentityContext } from '@orchard/frontend-identity';
import SplashPage from './pages/splash';
import NoProfilePage from './pages/noProfile';
import LoginFailedPage from './pages/loginFailed';
import AuthenticatedApp from './app';

export const App = ({ config }) => {
    const {
        auth0Domain,
        auth0ClientId,
        auth0Audience,
        auth0RedirectUri = window.location.origin,
        locale
    } = config;

    const { client, user, error } = useAuthFlow({
        // user.profile is set by this profile type ( optional )
        mainProfileType: 'InsightsProfile',
        clientOptions: {
            audience: auth0Audience,
            domain: auth0Domain,
            client_id: auth0ClientId,
            redirect_uri: auth0RedirectUri
        }
    });

    if (error)
        return <LoginFailedPage error={error} client={client} user={user} />;

    if (!user)
        return <SplashPage />;

    LocalizationManager.instance.setCurrentLocale(user.locale || locale);
    Sentry.setUser(user);
    Sentry.setExtras(config);

    if (!user.profile)
        return <NoProfilePage user={user} client={client} />;

    return (
        <IdentityContext.Provider value={{ user, client }}>
            <AuthenticatedApp />
        </IdentityContext.Provider>
    );
};
```

## `IdentityContext`
React context that holds the current user and client instance.
The IdentityContext needs to be added to the component hierarchy in order to access the current user and client instance.

```jsx
return (
    <IdentityContext.Provider value={{ user, client }}>
        <AuthenticatedApp />
    </IdentityContext.Provider>
);
```

## `useIdentity`
React hook that returns current user and client set in the IdentityContext.
If user is not available in the context, this function throws an error.

```jsx
import React from 'react';
import { useIdentity } from '@orchard/frontend-identity';

const LoggedInUserComponent = () => {
    const { user } = useIdentity();

    return <span>{user.name} / {user.email}</span>;
};
```

## `useUser`
React hook that returns current user. If user is not yet available in the IdentityContext, then this function returns undefined.

```jsx
import React from 'react';
import { userUser } from '@orchard/frontend-identity';

const LoggedInUserComponent = () => {
    const user = userUser();
    if( user )
        return <span>{user.name} / {user.email}</span>;
    return <span>Not logged in</span>;
};
```

## `withUser`
React HOC that provides user instance as a property.

```jsx
import React from 'react';
import { withUser } from '@orchard/frontend-identity';

const LoggedInUserComponent = ({ user }) =>
    <span>{user.name} / {user.email}</span>;

export default withUser(LoggedInUserComponent);
```
---
## Apollo client integration
```js
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import * as Sentry from '@sentry/browser';

const createApolloClient = (auth0Client, user) => new ApolloClient({
    uri: '/grass/graphql-product/graphql',
    cache: new InMemoryCache(),
    request: async (operation) => {
        let silentToken;
        try {
            silentToken = await auth0Client.getTokenSilently();
        } catch (e) {
            if (e.error) { // an auth0 error
                Sentry.captureMessage(e.error);

                // causes an redirect to login screen
                auth0Client.logout();
            }
        }

        return operation.setContext({
            headers: {
                ...operation.getContext().headers,
                Authorization: `Bearer ${silentToken}`,
                'Orchard-Profile-Type': user.profile.type,
                'Orchard-Profile-Id': user.profile.id
            }
        });
    }
});
```

```jsx
import React from 'react';
import { ApolloProvider } from '@apollo/react-hooks';
import { BrowserRouter } from 'react-router-dom';
import createApolloClient = from './apollo/client';

const AuthenticatedApp = ({ client, user }) => {
    const apolloClient = createApolloClient(client, user);

    return (
        <ApolloProvider client={ apolloClient }>
            <BrowserRouter>
                <App />
            </BrowserRouter>
        </ApolloProvider>
    );
};
```

---
## Feature flags
The user class has a few helper functions to help out with feature flags.
As the JWT does not contain any feature flags, the application itself needs to load/fetch them.

```jsx
import React from 'react';
import { useIdentity } from '@orchard/frontend-identity';
import { useFeaturesQuery } from './apollo/queries';
import ErrorMessage from './components/errorMessage';
import PageLoadingIndicator from './components/loadingIndicator';
import AppWithFeatures from './appWithFeatures';

const App = () => {
    const { loading, error, data } = useFeaturesQuery();
    const { user } = useIdentity();

    if (error)
        return <ErrorMessage error={error} />;

    if (loading)
        return <PageLoadingIndicator />;

    // supports a dictionary or an array of feature flags.
    // data = [{ feature: 'version_2', value: 'enabled' }]
    // data = { version_2: 'enabled', version_3: 'control' };
    user.setFeatures(data);

    return <AppWithFeatures />;´
};
```

```jsx
import React from 'react';
import { useIdentity } from '@orchard/frontend-identity';

import SubComponentV2 from './subComponentV2';
import SubComponent from './subComponent';

const SomeComponent = () => {
    const { user } = useIdentity();

    if (user.hasFeature('version_v2'))
        return <SubComponentV2 />;
    return <SubComponent />;
};
```
