Skip to content

HTTP React

1. DNext React HTTP

@dnext-react/http is a library that simplifies authentication with Keycloak in React applications. It provides a DNextOidcProvider component that wraps your application and manages the Keycloak instance. The package also includes an httpClient instance that automatically attaches the authentication token to HTTP requests.

2. Import and Configure DNextOidcProvider

The DNextOidcProvider component from @dnext-react/http wraps your application and provides Keycloak authentication context to all child components.

2.1. Import DNextOidcProvider

In your main application file (e.g., src/App.tsx or src/index.tsx), import the DNextOidcProvider:

import React from 'react';
import ReactDOM from 'react-dom';
import { DNextOidcProvider } from '@dnext-react/http';
import App from './App';

2.2. Configure Keycloak Define your Keycloak configuration object. This configuration should match the settings of your Keycloak server:

const keycloakConfig = {
    authority: 'https://your-keycloak-domain/auth/realms/your-realm',
    client_id: 'your-client-id',
    redirect_uri: window.location.origin + window.location.pathname.replace(/\/$/, ''),
    post_logout_redirect_uri: window.location.origin,
    response_type: 'code',
    scope: 'openid',
    automaticSilentRenew: true,
    disablePKCE: true,
    loadUserInfo: true,
};
  • authority: The URL of the OIDC/OAuth2 provider.
  • client_id: Your client application's identifier as registered with the OIDC/OAuth2.
  • redirect_uri: The redirect URI of your client application to receive a response from the OIDC/OAuth2 provider
  • post_logout_redirect_uri: The OIDC/OAuth2 post-logout redirect URI
  • response_type: The type of response expected from the OIDC/OAuth2 provider.
  • scope: The scope of the access request.
  • automaticSilentRenew: Whether to automatically renew the access token.
  • disablePKCE: Whether to disable Proof Key for Code Exchange (PKCE).
  • loadUserInfo: Whether to load user information after login.

2.3. Wrap Your Application with DNextOidcProvider

Wrap your application with the DNextOidcProvider, passing in the configuration:

ReactDOM.render(
  <DNextOidcProvider userManager={keycloakConfig} onSigninCallback={onSigninCallback}>
    <App />
  </DNextOidcProvider>,
document.getElementById('root')
);
import React from 'react';
import DNextOidcProvider from '@dnext-react/http/src/DNextOidcProvider';
import { onSigninCallback } from '@dnext-react/http';

const App: React.FC = () => {
const keycloakConfig = {
    authority: 'https://your-keycloak-domain/auth/realms/your-realm',
    client_id: 'your-client-id',
    redirect_uri: window.location.origin + window.location.pathname.replace(/\/$/, ''),
    post_logout_redirect_uri: window.location.origin,
    response_type: 'code',
    scope: 'openid',
    automaticSilentRenew: true,
    disablePKCE: true
    //...
};

  return (
    <DNextOidcProvider userManager={keycloakConfig} onSigninCallback={onSigninCallback}>
      {/* Your application components */}
    </DNextOidcProvider>
  );
};

export default App;

Alternatively, if you're using a BrowserRouter or other providers, make sure DNextOidcProvider is placed appropriately:

import React from 'react';
import HomeComponent from './features/home/HomeComponent';
import { BrowserRouter } from 'react-router-dom';

import DNextOidcProvider from '@dnext-react/http/src/DNextOidcProvider';
import { onSigninCallback } from '@dnext-react/http';


function App() {
  return (
  <React.StrictMode>
    <BrowserRouter basename="/">
      <DNextOidcProvider userManager={userManager as any} onSigninCallback={onSigninCallback}>
        {/* Your application components */}
      </DNextOidcProvider>
    </BrowserRouter>
  </React.StrictMode>
  );
}

3. Use the useAuth Hook

The dnextUseAuth hook provides access to authentication state and functions such as login and logout.

3.1. Import useAuth

In any component where you need to access authentication information, import the dnextUseAuth hook:

import React from 'react';
import dnextUseAuth from "@dnext-react/http/src/useAuth";

The dnextUseAuth hook provides the following properties and functions:

{
    readonly settings: UserManagerSettings;
    readonly events: UserManagerEvents;
    clearStaleState(): Promise<void>;
    removeUser(): Promise<void>;
    signinPopup(args?: SigninPopupArgs): Promise<User>;
    signinSilent(args?: SigninSilentArgs): Promise<User | null>;
    signinRedirect(args?: SigninRedirectArgs): Promise<void>;
    signinResourceOwnerCredentials(args: SigninResourceOwnerCredentialsArgs): Promise<User>;
    signoutRedirect(args?: SignoutRedirectArgs): Promise<void>;
    signoutPopup(args?: SignoutPopupArgs): Promise<void>;
    signoutSilent(args?: SignoutSilentArgs): Promise<void>;
    querySessionStatus(args?: QuerySessionStatusArgs): Promise<SessionStatus | null>;
    revokeTokens(types?: RevokeTokensTypes): Promise<void>;
    startSilentRenew(): void;
    stopSilentRenew(): void;
}

3.2. Use the Hook in a Component

Here's an example of how to use dnextUseAuth in a component:

import dnextUseAuth, { hasAuthParams } from '@dnext-react/http/src/useAuth';

const Profile: React.FC = () => {
  const { isLoading, isAuthenticated, user, activeNavigator, error } = dnextUseAuth();

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

  if (!isAuthenticated) {
    return <button onClick={() => auth.signinRedirect()}>Login</button>;
  }

  return (
    <div>
      <h1>Profile</h1>
      <p>Token: {token}</p>
      {auth.user?.profile.map((item) => (
        <div key={item.key}>
          <strong>{item.key}:</strong> {item.value}
        </div>
      ))}
      <button onClick={() => logout()}>Logout</button>
    </div>
  );
};

export default Profile;

Explanation:

  • isAuthenticated: Boolean indicating if the user is authenticated.
  • isLoading: Boolean indicating if the authentication state is still loading.
  • user: The user object containing profile information.
  • activeNavigator: Tracks the status of most recent signin/signout request method.
  • error: Was there a signin or silent renew error?
  • hasAuthParams: Check if the URL has authentication parameters.

4. Make Authenticated HTTP Requests

Use the httpClient instance from @dnext-react/http to make HTTP requests that automatically include the authentication token.

4.1. Import httpClient

In your service or component where you need to make HTTP requests:

import { httpClient } from '@dnext-react/http';

4.2. Make Requests

Use httpClient to make requests as you would with Axios:

const fetchData = async () => {
  try {
    const response = await httpClient.get('/api/data');
    console.log(response.data);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
};

Note:

  • The httpClient automatically attaches the authentication token to the Authorization header.
  • It also handles token refresh logic, ensuring that your requests have a valid token.

5. Protect Routes

If you're using React Router, you can protect routes by checking the authentication state.

5.1. Create a Protected Route Component

import React from 'react';
import { Route, Redirect, RouteProps } from 'react-router-dom';
import dnextUseAuth from "@dnext-react/http/src/useAuth";

interface ProtectedRouteProps extends RouteProps {
  component: React.ComponentType<any>;
}

const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ component: Component, ...rest }) => {
  const { isAuthenticated, isLoading } = dnextUseAuth();

  if (!initialized) {
    return <div>Loading...</div>;
  }

  return (
    <Route
      {...rest}
      render={(props) =>
        isAuthenticated ? (
          <Component {...props} />
        ) : (
          login() || <Redirect to="/" />
        )
      }
    />
  );
};

export default ProtectedRoute;

5.2. Use the Protected Route

In your routing configuration:

import ProtectedRoute from './ProtectedRoute';
import Dashboard from './components/Dashboard';

const AppRoutes: React.FC = () => (
  <Switch>
    <Route exact path="/" component={Home} />
    <ProtectedRoute path="/dashboard" component={Dashboard} />
    {/* Other routes */}
  </Switch>
);

6. Access User Information

You can access additional user information from the Keycloak instance if needed.

6.1. Retrieve User Profile

import React from 'react';
import dnextUseAuth from "@dnext-react/http/src/useAuth";

const UserProfile: React.FC = () => {
  const { initialized, isAuthenticated, user } = dnextUseAuth();

  if (!initialized) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    return <div>User is not authenticated.</div>;
  }

  if (!profile) {
    return <div>Loading profile...</div>;
  }

  return (
    <div>
      <h2>
        Welcome, {profile.family_name} {profile.given_name} {profile.family_name}
      </h2>
      <p>preferred_username: {profile.preferred_username}</p>
      <p>name: {profile.name}</p>
      <p>email: {profile.email}</p>
    </div>
  );
};

export default UserProfile;

Explanation:

  • profile: The user profile object containing information such as name, email, and other attributes.
  • initialized: Boolean indicating if the user profile has been loaded.
  • isAuthenticated: Boolean indicating if the user is authenticated.

7. Handle Token Refresh and Errors

The httpClient handles token refresh automatically, but you can customize error handling if needed.

7.1. Customize Axios Interceptors

If you need to handle specific error scenarios, you can modify the httpClient interceptors.

// In your project, create a custom httpClient if needed

import { setAuth } from '@dnext-react/http/httpClient';
import dnextUseAuth from "@dnext-react/http/src/useAuth";

const auth = dnextUseAuth();
setAuth(auth);

8. Ensure Singleton Keycloak Instance

As discussed earlier, it's important that the Keycloak instance is a singleton across your application.

8.1. Use the Provided getKeycloakInstance Function

When you need to access the Keycloak instance directly, import getKeycloakInstance:

import { httpClient } from '@dnext-react/http/httpClient';

const httpClient = httpClient.getInstance();

httpClient: AxiosInstance A pre-configured Axios instance for making HTTP requests.

Supported Methods: - httpClient.get(url: string, config?: AxiosRequestConfig) - httpClient.post(url: string, data?: any, config?: AxiosRequestConfig) - httpClient.put(url: string, data?: any, config?: AxiosRequestConfig) - httpClient.delete(url: string, config?: AxiosRequestConfig) - httpClient.patch(url: string, data?: any, config?: AxiosRequestConfig) - httpClient.head(url: string, config?: AxiosRequestConfig) - httpClient.options(url: string, config?: AxiosRequestConfig)

9. Environment Configuration

For different environments (development, staging, production), you might want to manage your Keycloak configuration accordingly.

9.1. Use Environment Variables

Set up environment variables in your React application:

// .env.development
REACT_APP_ISSUER=https://dev-keycloak.example.com/auth
REACT_APP_KEYCLOAK_CLIENT_ID=my-client-id
    // ...
// src/config/keycloakConfig.ts
const keycloakConfig = {
    authority: process.env.REACT_APP_ISSUER,
    client_id: process.env.REACT_APP_KEYCLOAK_CLIENT_ID,
    // ...
};

export default keycloakConfig;

10. Conclusion

By following these steps, you can successfully integrate @dnext-react/http into your React application. The package simplifies authentication with Keycloak and provides an HTTP client that manages tokens and authentication headers automatically.

Note: Ensure that all components consuming the useAuth hook or accessing the Keycloak instance are nested within the DNextOidcProvider.