KeyCloak-js and vue3 - Need some pages to be public

2.1k Views Asked by At

I'm using keycloak-js adapter with a Vue 3 Application. This system has some public pages, so I can't call keycloak right away. I already setup a button to call the login page, but the token is not being setup in the localStorage.

Basically my user flow need to be: Landing page > Clicks login button > Go to keycloak login page > redirects to a page where i make a request to return the user roles > get the user rows and redirects to the right page.

My request need a token and I can get to the redirection page, but the request fails because I don't have a token or a refresh token. Also, it give TypeError: adapter is undefined error when I try something different

Heres is my keycloak init function. If I change the onLoad option to check-sso, it returns #error=login_required&state=c7d6d227-5b23-4b21-962a-8e6291545f55. So login-required is working for me.

const Login = () => {
    KeyCloakInstance.init({ onLoad: "login-required", checkLoginIframe: true, 
    redirectUri: window.location.origin + "/redirect" })
    .then((authenticated) => {
        console.log("Keycloak Authenticated", authenticated)
        if (authenticated) {
            console.log("Authentication Success")
            KeyCloakInstance.authenticated = true;
        } else {
            console.log("Authentication Failed")
        }

        localStorage.setItem('token', KeyCloakInstance.token as string);
        localStorage.setItem('refreshToken', KeyCloakInstance.refreshToken as string);

    }).catch((err) => {
        console.dir(err);
        console.log("Authentication Failed", err);
    });
}

and heres a guard I made for the routes

import { getRole } from "@/services/config";
import Keycloak from "keycloak-js";

function guardAuth(to: any, from: any, next: any){
    const hasUser = localStorage.getItem("token");
    const isMeta = "auth" in to.meta;
    const metaAuth = to.meta.auth;

    if(isMeta && metaAuth && !hasUser){
        const keycloakInst = new Keycloak({
            url: "http://localhost:8082/",
            realm: "APP",
            clientId: "live_app"
        });

        keycloakInst.login();
    } else if(isMeta && !metaAuth && hasUser){
        RoleType(hasUser, next);
    } else {
        next();
    }
}

function RoleType(user: any, next: any): void{
    if(getRole() == user.role_portal){
        next('/redirect');
    } else {
        next('/');
    }
}

export { guardAuth };

I tried changing the keycloak init options and making guards for my routes, so I can check is has a token, but they also doesnt seem to work. I expect to have a flow where some pages are public and other are protected by keycloak

1

There are 1 best solutions below

0
On

This is how I have setup keycloak in my Vue.js app. I have two pages that are public and the rest of them are private and accessed after keycloak authentication.

    import Keycloak from "keycloak-js";
    import useStore from "./store";
    
    const initOptions = {
      realm: import.meta.env.VITE_KEYCLOAK_REALM_NAME,
      clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
      url: import.meta.env.VITE_KEYCLOAK_SERVER_URL,
      "public-client": true,
      "verify-token-audience": false,
    };
    
    const keycloak = Keycloak(initOptions);
    
    async function initKeycloak() {
      const store = useStore();
    
      await keycloak
        .init({onLoad: "check-sso", redirectUri: import.meta.env.VITE_KEYCLOAK_REDIRECT_URI})
        .then(async (auth) => {
          if (!auth) {
            store.unauthenticate();
          } else {
            await store.authenticate();
            if (keycloak.token) {
              store.token = keycloak.token;
              store.userRoles = keycloak.tokenParsed.realm_access.roles;
              window.localStorage.setItem("keycloakToken", keycloak.token);
            }
          }
        });
      setInterval(() => {
        keycloak
          .updateToken(70)
          .then((refreshed) => {
            if (refreshed) {
              store.token = keycloak.token;
              window.localStorage.setItem("keycloakToken", keycloak.token);
              console.info("Token refreshed" + refreshed);
            } else {
              console.warn(
                "Token not refreshed, valid for " +
                  Math.round(
                    keycloak.tokenParsed.exp +
                      keycloak.timeSkew -
                      new Date().getTime() / 1000
                  ) +
                  " seconds"
              );
            }
          })
          .catch(() => {
            console.error("Failed to refresh token");
          });
      }, 3000);
    }
    
    export async function keycloakLogin() {
      keycloak.login();
    }
    
    // keycloak logout
    var logoutOptions = {redirectUri: import.meta.env.VITE_KEYCLOAK_REDIRECT_URI};
    
    function keycloakLogout() {
      keycloak
        .logout(logoutOptions)
        .then((success) => {
          console.log("--> log: logout success ", success);
        })
        .catch((error) => {
          console.log("--> log: logout error ", error);
        });
    }

I'm not sure why you're using the KeycloakInstance maybe you've declared it on top of the file. I'm just using the keycloakLogin method on my landing page which is public and has the login button.

On click of the login button the keycloakLogin method is called and it redirects to the auth server. And after successful authentication it redirects to the redirectUri which is present in my .env.

I hope this helps.