SohoHouse / nuxt-oauth

Simple OAuth2 integration for your Nuxt app
MIT License
122 stars 27 forks source link

Callback after successful login #23

Closed thattejada closed 6 years ago

thattejada commented 6 years ago

How can I call a custom function right after the user logs in?

samtgarson commented 6 years ago

The fetchUser hook can be used for this I think, it is called only if a user has successfully logged in.

thattejada commented 6 years ago

Thanks @samtgarson, but, how can I call that method in my nuxt app immediately the OAuth flow is completed?, can you give me any example? I can't figure out how to do that

samtgarson commented 6 years ago

I'm not actually sure what your requirement is. You can put any logic you want to be triggered as soon as the user has logged in in any component or store (e.g. in a created method or using nuxtServerInit) and it will be called when the app boots.

You know whether or not the user has logged in based on if the OAuth vuex module has been populated.

thattejada commented 6 years ago

What I'm trying to do is:

But I can't get the fetchUser method triggered

This is my nuxt.config.js:

  oauth: {
    sessionName: 'appSession',
    secretKey: process.env.SECRET_KEY,
    oauthHost: process.env.OAUTH_HOST,
    oauthClientID: process.env.OAUTH_CLIENT_ID,
    oauthClientSecret: process.env.OAUTH_CLIENT_SECRET,
    scopes: process.env.OAUTH_CLIENT_SCOPE.split(','),
    accessTokenPath: '/access_token',
    getOauthHost: req => req.params.oauth_host,
    onLogout: (req, res) => {
      // do something after logging out
    },
    fetchUser: (accessToken, request) => {
      /**
       * access to the store and send the token to
       * the backend
       */
    }
  },

I'm sorry a lot but I'm newbie to Nuxt and Vue world

samtgarson commented 6 years ago

In the top level of your store store/index.js, create an action like this:

nuxtServerInit ({ state, dispatch }) {
  const { accessToken } = state.oauth
  if (accessToken) {
    // the user is logged in, dispatch your action
    // to send token to backend
    dispatch('fetchAccessToken', accessToken)
  } else {
    // the user is not logged in
  }
}

Nuxt will call this action on the server side as part of the initial load of the app.

thattejada commented 6 years ago

Thanks @samtgarson, but, I tried to implement the nuxtServerInit in store/index.js this way:

export const actions = {
  nuxtServerInit ({ state, dispatch }) {
    console.log("The state", state) // <-- I'm logging the state to see if oauth module is loaded, but it's not
    const { accessToken } = state.oauth

    if (accessToken) {
      console.log("logged in")
      // the user is logged in, dispatch your action
      // to send token to backend
      dispatch('fetchAccessToken', accessToken)
    } else {
      // the user is not logged in
      console.log("not logged in")
    }
  }
}

The problem is that the state is not loading / showing the oauth module: The console.log("The state", state) shows:

The state { auth:
   { accessToken: null,
     expiresIn: null,
     tokenType: null,
     refreshToken: null },
  groups: { groupList: [] },
  messages:
   { messageList: [],
     messageTypeToRequest: '',
     selectedMessage: {},
     newMessage: '' },
  users: { usersList: [], userGroup: 'all' } }

Those are the store modules I have: screen shot 2018-07-19 at 9 49 09 am

But the oauth module is not being loaded in that moment when the nuxtServerInit method runs

My whole nuxt.config.js is:

require('dotenv').config()

module.exports = {
  modules: [
    'nuxt-oauth',
    '@nuxtjs/axios',
  ],
  /*
  ** Headers of the page
  */
  head: {
    title: 'Buz',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: 'my description' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' }
    ]
  },
  /*
  ** Customize the progress bar color
  */
  loading: { color: '#3B8070' },
  plugins: [
    '~plugins/vuetify.js',
    '~/plugins/axios.js',
    // '~/plugins/vue-tags-input.js',
  ],
  css: [
    '~assets/app.styl',
    // '@voerro/vue-tagsinput/dist/style.css'
  ],
  /*
  ** Build configuration
  */
  build: {
    publicPath: 'http://localhost/buz/',
    vendor: [
      'axios', 
      'vuetify'
    ],
    /*
    ** Run ESLint on save
    */
    extend (config, { isDev, isClient }) {
      /*if (isDev && isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }*/
    }
  },
  axios: {
    // See https://github.com/nuxt-community/axios-module#options
    baseURL: process.env.BASE_URL,
  },  
  oauth: {
    sessionName: 'appSession',
    secretKey: process.env.SECRET_KEY,
    oauthHost: process.env.OAUTH_HOST,
    oauthClientID: process.env.OAUTH_CLIENT_ID,
    oauthClientSecret: process.env.OAUTH_CLIENT_SECRET,
    scopes: process.env.OAUTH_CLIENT_SCOPE.split(','),
    // authorizationPath: '/authorize',
    accessTokenPath: '/access_token',
    getOauthHost: req => req.params.oauth_host,
    onLogout: (req, res) => {
      // do something after logging out
    },
    fetchUser: (accessToken, request) => {
      // do something to return the user
      const user = User.findByToken(accessToken, request)
      return user
    }
  },

}
samtgarson commented 6 years ago

@diegotejadav have you upgraded to 2.0.0?

Also: unless you have a User.findByToken method defined somewhere, this will not work: const user = User.findByToken(accessToken, request)

You need to define your own fetchUser method if you want nuxt-oauth to populate the store with your user object

thattejada commented 6 years ago

Thank you so much @samtgarson, I was outdated with the 1.1.0 version, after that all the pieces you suggested these days fit for a final solution.

Now when my user authenticates with the authorization server, immediately it is authenticated with the resource server, and I can access to the endpoints I need.

I realized that I need more of the async world and promises with JS.

If someone need an approach like this, the solution was:

store/index.js:

export const actions = {
  async nuxtServerInit ({ state, dispatch }) {
    const { accessToken } = state.oauth

    if (accessToken) {
      console.log("logged in")
      const success = await dispatch('auth/convertToken', accessToken)
    } else {
      console.log("not logged in")
    }
  }
}

store/auth.js:

export const state = () => ({
    accessToken: null,
    expiresIn: null,
    tokenType: null,
    refreshToken: null
})

export const mutations = {    
    setConvertData (state, data) {        
        state.accessToken = data['access_token']
        state.expiresIn = data['expires_in']
        state.tokenType = data['token_type']
        state.refreshToken = data['refresh_token']        
    }
}

export const actions = {
    convertToken ({ commit, state }, accessToken) {
        const params = {
            grant_type: 'convert_token',
            client_id: '<client_id>',
            client_secret: '<client_secret>',
            backend: '<backend-name>-oauth2',
            token: accessToken
        }
        return this.$axios.post('/auth/convert-token', params)
            .then((res) => {
                commit('setConvertData', res.data)
            })
    }    
}