Search code examples
vuejs2identityserver4openid-connectvue-routeroidc-client-js

Vue js integration with identity server 4


I am implementing identity server 4 with vue js in client side.

Currently, I have only the admin login in the application, with the password and username.

I do not have any public site from where the USER get redirected to login. I wanted the user get redirected to login page directly when they hit the application client URL.

Currently in the sample version of identity server 4 the user needs to hit the login button on the navbar and redirected to sign in.

I wanted to do in the following ways:

  1. Client Hit the URL. Check whether the user is authenticated or not.
  2. Redirect to Identity Server.
  3. Sign In with Username/Password
  4. Redirect to client portal after successful login.

I have very hard time to integrate this approach in vue js.

With my current implementation the application is always running in the loop and it is always redirected to sign in.

store.getters.isAuthenticated is never true and it always redirecting the application with SignIn.

I couldn't figure out whether i have missed something in my router.beforeEach or something in the implementation of oidc.

Routes:

export const routes = [
  {
    component: Full,
    path: "/",
    meta: {
      requiresAuth: true
    },
    children: [
      {
        path: "/dashboard",
        name: "Dahboard",
        component: DashboardView,
        meta: {
          requiresAuth: true
        }
      },
      {
        path: "/signin",
        component: Signin
      },
      {
        path: "/company",
        component: Company,
        meta: {
          requiresAuth: true
        }
      }
    ]
  }
];

import Vue from "vue";
import VueRouter from "vue-router";
import { routes } from "./routes";
import store from '../store'

Vue.use(VueRouter);

let router = new VueRouter({
  mode: "history",
  routes
});

export default router;
router.beforeEach((to, from, next) => {
  if (store.getters.isAuthenticated) {
    const notValid = ['/signin', '/signup']

    if (notValid.lastIndexOf(to.path) >= 0) {
      next({
        path: '/dashboard'
      })
    } else if (to.path === '/signout') {
      store.commit('removeAuthentication')
      next({
        path: '/'
      })
    } else {
      next()
    }
  } else if (to.matched.some(record => record.meta.requiresAuth)) {
    next({
      path: '/signin',
      query: {
        redirect: to.fullPath
      }
    })
  } else if (to.path === '/signout') {
    next({
      path: '/'
    })
  } else {
    next()
  }
})

SignIn Component:

import authService from './authService';

export default {
    name: 'Auth',
    mounted() {
        authService.login();
        authService.completeLogin();
    }
};

Config.js

export default {
    authority: "https://localhost:44350",
    client_id: "js",
    redirect_uri: `${domain}/signin`,
    response_type: "id_token token",
    scope:"openid profile api1",
    post_logout_redirect_uri : `${domain}`,
    silent_redirect_uri: `${domain}/silent.html`,
}

Login: this.userManager.signinRedirect()

completeLogin: this.userManager.signinRedirectCallback()

Silent.html:

new Oidc.UserManager().signinSilentCallback()
.catch(function (error) {
    console.error(error);
});

Solution

  • I'm not sure I understand everything correctly and I never worked with Vue. But let me describe a typical implicit flow which I assume, you want to achieve here:

    1. The user will hit your site unauthenticated (e.g. yoursite.com/dashboard)
    2. The AuthService will try to sign in the user - i.e. he/she will be redirected to the login page of identity server
    3. If the user does not have a valid session on identity server he/she will be promted for credentials. Otherwise he/she will be immediately redirected back to the requested redirect uri of your site (e.g. yoursite.com/signin).
    4. At this point the user is back at your site but still not logged in. You will notice that the url will contain a lot of additional text after the '#' character (e.g. yoursite.com/singin#access_token=ey456...)
    5. When you call completeLogin() oidc-client will read this data and save it (e.g. in your browsers session storage) - after this method completes, your user is logged in and you can redirect him/her to the original site (e.g. yoursite.com/dashboard)

    You probably get an infinite loop between steps 1 - 4, because you never allow step 5 to happen, so the login never completes.

    Try to change your signin component

    import authService from './authService';
    
    export default {
        name: 'Auth',
        mounted() {
            //authService.login(); -- This line will redirect the user again before the login can complete
            authService.completeLogin();
        }
    };
    

    Also make sure the signin component can be accessed by the user even if he/she is not yet authenticated, so he/she can complete the login process.