JoseGoncalves / vue-keycloak

Keycloak plugin for Vue 3 with Composition API
Apache License 2.0
11 stars 4 forks source link

Detecting if the Keycloak Server is available? #1

Closed flowt-au closed 10 months ago

flowt-au commented 10 months ago

Hi. Thanks for keeping this package going. :-)

I am trying to work out how best to check if the KC server is available before I try to use it. At present, if the KC endpoint is not reachable my app navigates to the configured endpoint and throws ERR_CONNECTION_REFUSED.

It would be great to be able to check first, stay on the app's domain and handle the error gracefully in my app, rather than the user being marooned on the KC url.

The KC docs and chatter talks about using messageReceiveTimeout (defaults to 10secs) which can be set in options. But how to catch that?

Here is my current config (using it with Quasar)

import { vueKeycloak } from '@josempgon/vue-keycloak'

const keycloakServerUrl = httpprotocol + kc_domain + kc_port

const res = await app.use(vueKeycloak, {
  initOptions: {
    flow: 'standard', // default
    checkLoginIframe: false, // default
    onLoad: 'login-required', // default
    messageReceiveTimeout: 1000
  },
  config: {
    url: keycloakServerUrl,
    realm: kc_realm,
    clientId: kc_client
  }
})

Any suggestions? Thanks, Murray

JoseGoncalves commented 10 months ago

Hi @flowt-au. I think the best approach to check the KC availability before redirecting to keycloak for authentication would be to use the well-known endpoint. Use an HTTP library to issue a GET on that endpoint before initiating the authentication process and you could then handle the KC not available error on your app.

flowt-au commented 10 months ago

Thank you. Yes, this is what I did and it works well.

const keycloakServerUrl = httpprotocol + kc_domain + kc_port
const keycloakWellKnownUrl = httpprotocol + kc_domain + kc_port + '/realms/' + kc_realm + '/.well-known/openid-configuration'

fetch(keycloakWellKnownUrl)
  .then(async response => {
    if (response.ok) {
      console.log('Keycloak server is up', response);

      const res = await app.use(vueKeycloak, {
        initOptions: {
          flow: 'standard', // default
          checkLoginIframe: false, // default
          onLoad: 'login-required' // default
        },
        config: {
          // Set in yourappconfig/ServerConfig.js
          url: keycloakServerUrl,
          realm: kc_realm,
          clientId: kc_client
        }
      })

    } else {
      console.error('Keycloak server is down', response);
      // Take some action...
    }
  })
  .catch(error => {
    console.error('Error pinging Keycloak server:', error)
    // Take some action...
  });
JoseGoncalves commented 10 months ago

An alternative that does not block your app entry point (e.g. src/main.js), would be to initialize the vue-keycloak plugin in a way that it does not trigger an automatic login:

import { vueKeycloak } from '@josempgon/vue-keycloak'
import { kc_url, kc_realm, kc_client } from '<path_to_some_config_file>';

app.use(vueKeycloak, {
    initOptions: {
        onLoad: null,
    },
    config: {
        url: kc_url,
        realm: kc_realm,
        clientId: kc_client,
    }
})

and then, in your main Vue SFC (e.g. src/App.vue) you perform the login only if you can reach the well-known endpoint:

<script setup>
import { watch } from 'vue';
import { useKeycloak } from '@josempgon/vue-keycloak';
import { kc_url, kc_realm } from '<path_to_some_config_file>';

const { isAuthenticated, isPending, keycloak } = useKeycloak();

const keycloakWellKnownUrl = kc_url + '/realms/' + kc_realm + '/.well-known/openid-configuration';

watch([isAuthenticated, isPending], ([authenticated, pending]) => {
    if (authenticated || pending) return;

    fetch(keycloakWellKnownUrl)
        .then(response => {
            if (response.ok) {
                keycloak.login();
            } else {
                console.error('Keycloak server is not working properly:', response);
                // Take some action...
            }
        })
        .catch(error => {
            console.error('Keycloak server is unreachable:', error);
            // Take some action...
        });
});
</script>
flowt-au commented 10 months ago

Oh, yes, I see. I didn't realise one could defer the authentication like that. Cool! Thanks. :-)

flowt-au commented 10 months ago

Just a followup: It turns out that fetching the wellknownurl can cause some flow issues. It worked fine on my local dev machine but for some reason (not yet understood) when used the approach on my server I had the issue below. My Vue app is in a subdomain served by Apache and is using the Vue recommended .htaccess settings.

Wrapping the keycloak initialisation inside the fetch as I was doing (above) caused Keycloak to loop "infinitely" once the user was authenticated. I removed the fetch "wrapper" and all worked well.

Anyway, just a heads-up in case anyone else follows my pattern above.

I will play some more with other approaches (eg the suggested delayed login above) to see how to make this work and report back.

Thanks, Murray