Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong redirect by using vue-kecloak with quasar #94

Closed
Excel1 opened this issue Jul 9, 2021 · 19 comments
Closed

Wrong redirect by using vue-kecloak with quasar #94

Excel1 opened this issue Jul 9, 2021 · 19 comments

Comments

@Excel1
Copy link

Excel1 commented Jul 9, 2021

Finally i got it working with quasar. But i got at least one problem:
If i logged in i will be redirected to the following url which leads to the 404 quasar error page instead to http://192.168.178.44:8080/.

http://192.168.178.44:8080/#/&state=671a7344-b7a0-4779-8fde-da8c99c6aa4a&session_state=a5598f7f-c11c-41f3-a777-883ac33f6ca1&code=c88b4a26-4bc4-4aca-93b5-5305b490ac37.a5698g7f-c11c-41f3-a777-883ac93f6ca1.3cb8d30d-4783-425a-bf33-3d79613bed10

In the example vue3-keycloak it redirects correctly. I checked the router but i could not get the error source.

boot/keycloak.ts

import VueKeyCloak from '@dsb-norge/vue-keycloak-js'
import axios, { AxiosRequestConfig } from 'axios';
import { boot } from 'quasar/wrappers';

export default boot(async ({ app}) => {
  function registerTokenInterceptor () {
    axios.interceptors.request.use((config: AxiosRequestConfig) => {
      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/restrict-template-expressions
      config.headers['Authorization'] = `Bearer ${app.config.globalProperties.$keycloak.token}`

      return config
    }, error => {
      return Promise.reject(error)
    }) //null, { synchronous: true })
  }

  return new Promise(resolve => {
    app.use(VueKeyCloak, {
      init: {
        onLoad: 'check-sso', // or 'login-required'
        flow: 'standard',
        pkceMethod: 'S256',
        silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html',
        checkLoginIframe: false
      },
      config: {
        url: 'https://my.keycloak.com/auth',
        realm: 'myrealm',
        clientId: 'myclientid',
      },
      onReady() {
        registerTokenInterceptor()
        resolve()
      }
    })
  })
})

public/slient-check-sso.html

<html>
<body>
<script>
  parent.postMessage(location.href, location.origin)
</script>
</body>
</html>

routes.ts

import { RouteRecordRaw } from 'vue-router';

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: () => import('layouts/MainLayout.vue'),
    children: [{ path: '', component: () => import('pages/Index.vue') }],
  },

  // Always leave this as last one,
  // but you can also remove it
  {
    path: '/:catchAll(.*)*',
    component: () => import('pages/Error404.vue'),
  },
];

export default routes;
@baltom
Copy link
Contributor

baltom commented Jul 12, 2021

I don't have any good explanation. To me it does seem like keycloak and this plugin is doing what is expected.

Also your "catch all" route can just be * instead. You could also comment it out and try to debug some route matching with the different router guards. But again this does not seem like an issue with this plugin or keycloak/js in itself

@Excel1
Copy link
Author

Excel1 commented Jul 12, 2021

Thank you for help. Commenting out unfortunately did not make a difference. I also believe that keycloakjs does not cause the error. I'll check the Quasar repository.

@nucle
Copy link

nucle commented Jul 15, 2021

@Excel1 could u solve it?

Sorry i had no time. But i could take a look today.
Should i check it?

@Excel1
Copy link
Author

Excel1 commented Jul 15, 2021

@nucle No problem :)
Unfortunately I could not solve it yet. If you have time you can check it out.
Thanks for helping!

@Excel1
Copy link
Author

Excel1 commented Jul 20, 2021

@nucle I think i found the problem. Its the routermode:

quasar.conf.js

build: {
      vueRouterMode: 'history', // available values: 'hash', 'history'

By using "hash" i will get redirected wrong:

http://192.168.178.44:8080/#/&state=671a7344-b7a0-4779-8fde-da8c99c6aa4a&session_state=a5598f7f-c11c-41f3-a777-883ac33f6ca1&code=c88b4a26-4bc4-4aca-93b5-5305b490ac37.a5698g7f-c11c-41f3-a777-883ac93f6ca1.3cb8d30d-4783-425a-bf33-3d79613bed10

By using "history" i will get redirected correctly:
http://192.168.178.44:8080/#state=671a7344-b7a0-4779-8fde-da8c99c6aa4a&session_state=a5598f7f-c11c-41f3-a777-883ac33f6ca1&code=c88b4a26-4bc4-4aca-93b5-5305b490ac37.a5698g7f-c11c-41f3-a777-883ac93f6ca1.3cb8d30d-4783-425a-bf33-3d79613bed10

But this only happens by using quasar. By using the vue-typescript example in router hashmode it works correctly. And also without parameters in url.

@nucle
Copy link

nucle commented Jul 21, 2021

@Excel1 great!
We should verify the problem and when that's the case we have to create a ticket.
I will check this too.

BR

@Excel1
Copy link
Author

Excel1 commented Nov 18, 2021

@baltom Maybe this Question should be opened again, cause we found out that this is a problem by keycloak-js:
Quasar - hashmode leads into wrong redirect by using vue-keycloak

@baltom
Copy link
Contributor

baltom commented Nov 19, 2021

Indeed, i didn't catch that this was/is a hash issue in vue-router. In which case it's a duplicate of our first registered issue #1 im guessing you are hitting the same issue

@muei
Copy link

muei commented Dec 9, 2021

What's going on now? Have you solved it?

@Excel1
Copy link
Author

Excel1 commented Dec 9, 2021

@muei No, currently im using history-mode as workaround.

@lcmuniz
Copy link

lcmuniz commented Apr 13, 2022

I'm going through the same problem but only on the application's internal routes. In the root route "/" history mode works but any other route (eg http://localhost:8080/#/route1) the state query is added and breaks the application (eg http://localhost:8080/# /route1&state=12...)

I tried 'hash' and 'history' modes and neither worked. I'm using vue-router 4.0.0 and vue-keycloak-js 2.1.3-beta.

The code from the 'boot/keycloak.js' file:

export default boot(async ({ app, router, store }) => {
  async function tokenInterceptor() {
    axios.interceptors.request.use(
      (config) => {
        config.headers.Authorization = `Bearer ${app.config.globalProperties.$keycloak.token}`;
        return config;
      },
      (error) => {
        return Promise.reject(error);
      }
    );
  }

  return new Promise((resolve) => {
    app.use(VueKeyCloak, {
      init: {
        onLoad: "check-sso", 
        window.location.origin + "/silent-check-sso.html"
      },
      config: {
        url: "https://my-server/auth/",
        realm: "my-real",
        clientId: "test",
      },
      onReady: (keycloak) => {
        tokenInterceptor();
        resolve();
      },
    });
  });
});

@arcadeJHS
Copy link

Hi, trying resolving the same issue I come to the following solution in my router config code:

const removeKeycloakStateQuery = (to, from) => {
  const cleanPath = to.path
    .replace(/[&\?]code=[^&\$]*/, "")
    .replace(/[&\?]state=[^&\$]*/, "")
    .replace(/[&\?]session_state=[^&\$]*/, "");

  return { path: cleanPath, query: {}, hash: to.hash };
};

// ...
  {
    path: "/:catchAll(.*)*",
    component: () => import("src/pages/component.vue"),
    beforeEnter: [removeKeycloakStateQuery],
  }

@Nighthree
Copy link

@arcadeJHS Thank you for the solution, it saved me. This problem has been bothering me for a long time. I tried to modify your solution to meet my needs.

main.js :

import { createApp } from 'vue';

import Keycloak from 'keycloak-js';

import App from './App.vue';
import router from './router';

// keycloak init options
const initOptions = {
  url: 'https://oauth.drshinetech.com/auth/',
  realm: 'EnterpriseService',
  clientId: process.env.VUE_APP_KEYCLOAK_CLIENT,
  onLoad: 'login-required',
}
const _keycloak = Keycloak(initOptions);


// get router.push() needs URL value
const getVueRoute = () => {
  // get URL
  const location = window.location.href;
  // Return '/' without hash  
  if (!location.includes('#')) return '/';

  // Get URL string after hash 
  const start = location.indexOf('#');
  const end = location.length - 1;
  let vueRoute = location.substring(start + 1, end);
  // Check if all keycloak params exist
  // And custom route query should avoid keycloak query
  const keycloakParams = [
    vueRoute.includes('&code'),
    vueRoute.includes('&state'),
    vueRoute.includes('&session_state')
  ];
  const state = keycloakParams.every((item) => item);
  // if state=true, replace URL
  if (state) {
    vueRoute = vueRoute
      .replace(/[&\?]code=[^&\$]*/, "")
      .replace(/[&\?]state=[^&\$]*/, "")
      .replace(/[&\?]session_state=[^&\$]*/, "");
  }
  return vueRoute;
}

export const app = createApp(App);
_keycloak.init({ checkLoginIframe: false, onLoad: initOptions.onLoad }).then(async (auth) => {
  app.config.globalProperties.$keycloak = _keycloak;
  app.use(store);
  app.use(router);
  app.mount('#app');
  const vuePushRoute = getVueRoute();
  await router.push(vuePushRoute);
});

@Roriz
Copy link

Roriz commented Jul 14, 2022

This solution looks more like a work around them a solution, maybe this problem is related with the keycloak himself: keycloak/keycloak#8959

@Anniel07
Copy link

@nucle I think i found the problem. Its the routermode:

quasar.conf.js

build: {
      vueRouterMode: 'history', // available values: 'hash', 'history'

By using "hash" i will get redirected wrong:

http://192.168.178.44:8080/#/&state=671a7344-b7a0-4779-8fde-da8c99c6aa4a&session_state=a5598f7f-c11c-41f3-a777-883ac33f6ca1&code=c88b4a26-4bc4-4aca-93b5-5305b490ac37.a5698g7f-c11c-41f3-a777-883ac93f6ca1.3cb8d30d-4783-425a-bf33-3d79613bed10

By using "history" i will get redirected correctly: http://192.168.178.44:8080/#state=671a7344-b7a0-4779-8fde-da8c99c6aa4a&session_state=a5598f7f-c11c-41f3-a777-883ac33f6ca1&code=c88b4a26-4bc4-4aca-93b5-5305b490ac37.a5698g7f-c11c-41f3-a777-883ac93f6ca1.3cb8d30d-4783-425a-bf33-3d79613bed10

But this only happens by using quasar. By using the vue-typescript example in router hashmode it works correctly. And also without parameters in url.

Excellet I have a whole day to fix this problem, your solution help me a lot

@NoudH
Copy link

NoudH commented Nov 6, 2023

This issue has indeed to do with Vue Router conflicting with Keycloak (see keycloak keycloak/keycloak#14742).
We can circumvent this by not immediately creating the router on import, but instead through a function that is called in onReady.

Based on this comment we used to following solution:

router/index.ts

const initializeRouter = () => {
  const router = createRouter({
     // Options
  })

  router.beforeEach(() => {...})
  
  router.afterEach(() => {...})

  return router
}

export default initializeRouter 

main.ts

import initializeRouter from '@/router'

const app = createApp(App)

// Other app.use statements
app.use(...)

app.use(VueKeyCloak, {
  config: {
   // config
  },
  init: {
    // init options
  },
  onReady: () => {
    app.use(initializeRouter())
    app.mount('#app')
  },
})

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $keycloak: VueKeycloakInstance
  }
}

export default app

This will correctly remove the Keycloak hashes from the URL.

@Ali-Javanmardi
Copy link

@NoudH Thanks,

Your solution : #94 (comment) worked great.

@MiguelAleixo
Copy link

This: https://github.com/gnieser/vue-keycloak/blob/master/src/router/index.js resolve this problem, only change router index file, for me work perfectly

@lln78
Copy link

lln78 commented Oct 23, 2024

A simpler solution : when you init your vue-keycloak instance, add "responseMode: 'query'"
As explain in keycloak/keycloak#26405 (comment) it's a keycloak feature (and default) to respond with hash parameters that cause the problem with hash router.

export default boot(async ({ app, router, store }) => {
  app.use(VueKeyCloak, {
    init: {
      onLoad: 'check-sso',
      useNonce: false,
      checkLoginIframe: false,
      silentCheckSsoFallback: false,
      silentCheckSsoRedirectUri:
        window.location.origin + '/silent-check-sso.html',
      responseMode: 'query',
    },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests