'Authentication on Keycloak through REST API (Next JS)

I have a problem. I need to login in Keycloak, but i don't want to use default pages from Keycloak. I have pages for "sign-in" and "sign-up". I wrote requests and can recieve access token for user, but what i should to do next? I use @react-keycloak/ssr and keycloak-js

This is _app.tsx

import './globals.css';
import '@fortawesome/fontawesome-free/css/all.css'
import Head from 'next/head';
import { Header } from "../shared/components/header/header";
import Footer from "../shared/components/footer/footer";

import cookie from 'cookie';
import type { IncomingMessage } from 'http';
import type { AppContext } from 'next/app';
import { SSRKeycloakProvider, SSRCookies } from '@react-keycloak/ssr';


const keycloakCfg = {
  url: `https://${process.env.AUTH_HOST}/auth`,
  realm: 'myrealm',
  clientId: 'react-client',
}

export default function MyApp({ Component, pageProps, cookies }) {

  console.log(cookies);
  const initOptions = {
    onLoad: 'check-sso',
    checkLoginIframe: false,
    flow: 'implicit',
  }

  return (
      <>
        <Head>
          <link rel="preconnect" href="https://fonts.googleapis.com" />
          <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
          <link
            href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700;800&display=swap"
            rel="stylesheet" />
          <link rel="icon" type="image/png" href="/favicon.png" />
        </Head>
        <Header />
        <SSRKeycloakProvider
          keycloakConfig={keycloakCfg}
          persistor={SSRCookies(cookies)}
          initOptions={initOptions}
        >
          <Component {...pageProps} />
        </SSRKeycloakProvider>
        <Footer />
      </>
  );
}

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

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

sign-in/index.tsx

import Form from '../../shared/components/form/form';
import styles from './sign-in.module.scss';
import Input from '../../shared/components/input/input';
import Checkbox from '../../shared/components/checkbox/checkbox';
import Button from '../../shared/components/button/button';
import React, { useEffect } from 'react';
import Head from 'next/head';
import { PAGE_TITLES } from '../../shared/constants/titles.const';
import BottomPanel from '../../shared/components/bottom-panel/bottom-panel';
import Link from 'next/link';

import { useKeycloak } from '@react-keycloak/ssr';
import { KeycloakInstance } from 'keycloak-js';
import { useRouter } from 'next/router';
import { getToken } from '../../shared/utils/keycloak-functions';

export default function SignIn() {
  const links = [
    {
      text: 'News',
      url: '/news'
    },
    {
      text: 'Community',
      url: '/news'
    },
    {
      text: 'Development',
      url: '/news'
    }
  ];
  
  // if user is authenticated rediretc to '/'
  const router = useRouter();
  const { keycloak } = useKeycloak<KeycloakInstance>();
  useEffect(() => {
    if (keycloak?.authenticated) router.push('/');
  });
  
  async function login() {
    const tokens = await getToken();
    if (tokens) {
      console.log(tokens);
      router.push('/');
    } 
  }
  
  return (
    <>
      <Head>
        <title>{PAGE_TITLES.signIn}</title>
      </Head>
        <main className={styles.page}>
          <Form>
            <h2 className={styles.heading}>Sign in</h2>
            <label className={styles.formRow}>
              <span className={styles.label}>Email</span>
              <Input name="username"/>
            </label>
            <label className={styles.formRow}>
              <span className={styles.label}>Password</span>
              <Input type="password" name="password"/>
            </label>
            <div className={styles.checkboxOuter}>
              <Checkbox>Remember me</Checkbox>
            </div>
            <br />
            <div className={styles.buttonOuter}>
              <Button action={() => login() }>Login</Button>
            </div>
            <div className={styles.formFooter}>
              <Link href="/sign-up">
                <a>Register</a>
              </Link>
            </div>
          </Form>
          <BottomPanel links={links} />
        </main>
    </>
  );
}

and this is a function getToken

export async function getToken() {
    const tokenEndpoint = `https://localhost/auth/realms/myrealm/protocol/openid-connect/token`;
    const fields = document.querySelectorAll('input');
    const username = fields[1].value;
    const password = fields[2].value;
    const body = `client_id=${client_id}&username=${username}&password=${password}&grant_type=password&scope=openid`;
    let token;
    const res = await fetch(tokenEndpoint, {
      method: "POST",
      body: body,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }).then((response) => response.json())
      .then((responseJSON) => {
        token = responseJSON;
      });
    
    return token;
  }

Can i somehow set access token to initOptions in SSRKeycloakProvider? When i login through default page, Keycloak redirects me to http://localhost:3000/ and everything is OK. I can see idToken and refreshToken. But when i recive accessToken, keycloak doesn't consider me authorized. Maybe i should to set some values in cookie? Help me, please. I'm tired of this task. Thanks



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source