Next.js: prevent Showing index page before redirecting to Keycloak Login page

Viewed 1673

I've been working on Next.js project which authenticates with a Keycloak.

I used '@react-keycloak/ssr' library to implement frontend authentication.

Here's my _app.js code (Code Reference: https://github.com/react-keycloak/react-keycloak-examples/tree/master/examples/nextjs-app)

import cookie from 'cookie'
import * as React from 'react'
import type { IncomingMessage } from 'http'
import type { AppProps, AppContext } from 'next/app'

import { SSRKeycloakProvider, SSRCookies } from '@react-keycloak/ssr'

const keycloakCfg = {
  url: 'http://myauthurl/auth',
  realm: 'myrealm',
  clientId: 'myclientid',
}



interface InitialProps {
  cookies: unknown
}

function MyApp({ Component, pageProps, cookies }: AppProps & InitialProps) {
  const initOptions = {
    onLoad: 'login-required',
    checkLoginIframe: false
  }

  return (
    <SSRKeycloakProvider
      keycloakConfig={keycloakCfg}
      persistor={SSRCookies(cookies)}
      initOptions={initOptions}
    >
      <Component {...pageProps} />
    </SSRKeycloakProvider>
  )
}

function parseCookies(req?: IncomingMessage) {
  if (!req || !req.headers) {
    return {}
  }
  return cookie.parse(req.headers.cookie || '')
}

MyApp.getInitialProps = async (context: AppContext) => {
  // Extract cookies from AppContext
  return {
    cookies: parseCookies(context?.ctx?.req),
  }
}

export default MyApp

My goal was to redirect unauthenticated users to keycloak login page. It worked by adding 'initOptions'. However, before redirecting, application shows index page for one second.

EDIT: After writing code checks the state of authentication, I managed to hide components for users who are not logged in. However, after login success application shows blank page and keycloak is undefined.

import type { AppProps, AppContext } from "next/app";
import { SSRKeycloakProvider, useKeycloak } from "@react-keycloak/ssr";
import { Provider } from "urql";
import {
  keycloakConfig,
  initOptions,
  getPersistor,
  Keycloak,
} from "../libs/keycloak";
import { parseCookies } from "../libs/cookie";
import { useMemo } from "react";
import { createUrqlClient, ssrCache } from "../libs/urql";

interface Props extends AppProps {
  cookies: unknown;
  token?: string;
}

function MyApp({ Component, pageProps, cookies, token }: Props) {
  const urqlClient = useMemo(() => createUrqlClient(token), [token]);
  const {keycloak} = useKeycloak
  console.log(keycloak) //undefined after login success
  console.log(keycloak?.authenticated  ) //undefined after login success
  // SSR cache for urql
  if (pageProps?.urqlState) {
    ssrCache.restoreData(pageProps.urqlState);
  }
  return (
    <SSRKeycloakProvider
      keycloakConfig={keycloakConfig}
      persistor={getPersistor(cookies)}
      initOptions={initOptions}
    >
      <Provider value={urqlClient}>
        {keycloak?.authenticated && <Component {...pageProps} /> }
      </Provider>
    </SSRKeycloakProvider>
  );
}

MyApp.getInitialProps = async (context: AppContext) => {
  const keycloak = Keycloak(context?.ctx?.req);

  return {
    cookies: parseCookies(context?.ctx?.req),
    token: keycloak.token,
  };
};
export default MyApp;

1 Answers

It has to wait for a split seconds since it needs to check for authentication.. what is your desire behavior?

You can use the useKeycloak hook in your page to show the behavior you want (e.g. Redirecting to login page....)

const IndexPage = () => {
  const { keycloak } = useKeycloak<KeycloakInstance>()

  const loggedinState = keycloak?.authenticated ? (
    <span className="text-success">logged in</span>
  ) : (
    <span className="text-danger">NOT logged in</span>
  )

  const welcomeMessage =
    keycloak?.authenticated 
      ? `Welcome back!`
      : 'Welcome visitor. Please login to continue.'

  return (
    <Layout title="Home | Next.js + TypeScript Example">
      <h1 className="mt-5">Hello Next.js + Keycloak </h1>
      <div className="mb-5 lead text-muted">
        This is an example of a Next.js site using Keycloak.
      </div>

      <p>You are: {loggedinState}</p>
      <p>{welcomeMessage}</p>
    </Layout>
  )
}

export default IndexPage

Simplified example from https://github.com/react-keycloak/react-keycloak-examples/blob/master/examples/nextjs-app/pages/index.tsx

Related