React-google-login: Getting Refresh Token

Created on 7 Jul 2017  路  5Comments  路  Source: anthonyjgrove/react-google-login

I've tried adding prompt='consent', but I'm not seeing a refresh token returned in the response. I'm unclear on how to go about refreshing the id_token once the session has expired without forcing the user to continually re-login.

Most helpful comment

Had the same issue with the JWT token expiring and logging the user out. I cooked up a scheme to detect JWT expiration and refresh it when it has expired. You can configure longer logged in durations by increasing MAX_JWT_REFRESHES:

<head>
  <script src="https://apis.google.com/js/platform.js" async defer></script>
</head>
// in your app layout component or other component which is guaranteed to be available
export default class AppLayout extends Component {
  componentDidMount() {
    startJWTRefreshChecker()
  }

  render() {
    // ...
  }
}
import decode from 'jwt-decode'
import Promise from 'bluebird'

import AppConfig from 'config'
import { ID_TOKEN_KEY } from 'helpers/authHelper'

const MAX_JWT_REFRESHES = 10
const JWT_REFRESH_COUNT_KEY = 'jwt_refresh_count'
export const CHECK_JWT_REFRESH_TIMER_KEY = 'check_jwt_refresh_timer'

export function getJWTExpirationDate(token) {
  const decoded = decode(token)
  const date = new Date(0) // The 0 here is the key, which sets the date to the epoch

  if(!decoded.exp) {
    return null
  }

  date.setUTCSeconds(decoded.exp)

  return date
}

export function checkJWTRefresh() {
  const token = localStorage.getItem(ID_TOKEN_KEY)

  console.log(`checkJWTRefresh...`)

  if (!shouldRefreshJWT()) {
    console.log('Maxed out JWT refreshes...')
    return stopJWTRefreshChecker()
  }

  if (isJWTExpired(token)) {
    refreshExpiredJWT()
  }
}

export function shouldRefreshJWT() {
  return getJWTRefreshCount() < MAX_JWT_REFRESHES
}

export function startJWTRefreshChecker() {
  // start the refresh checker (checking once / sec)
  return localStorage.setItem(CHECK_JWT_REFRESH_TIMER_KEY, setInterval(checkJWTRefresh, 1000))
}

export function stopJWTRefreshChecker() {
  console.log('Disabling refresh check...')

  return clearInterval(localStorage.getItem(CHECK_JWT_REFRESH_TIMER_KEY))
}

export function resetJWTRefreshCount() {
  console.log('Resetting refresh count...')

  return localStorage.setItem(JWT_REFRESH_COUNT_KEY, 0)
}

export function isJWTExpired(token) {
  const date = getJWTExpirationDate(token)

  if (date === null) {
    return false
  }

  return !(date.valueOf() > new Date().valueOf())
}

export function refreshExpiredJWT() {
  window.gapi.load('auth2', function () {
    const existingAuthInstance = window.gapi.auth2.getAuthInstance()

    let currentUserPromise

    if (existingAuthInstance) {
      currentUserPromise = Promise.resolve(existingAuthInstance.currentUser.get())
    } else {
      currentUserPromise = window.gapi.auth2.init({
        client_id: AppConfig.googleClientId,
        scope: AppConfig.googleClientScopes
      }).then(function (res) {
        return res.currentUser.get()
      })
    }

    return currentUserPromise
      .then((currentGoogleUser) => {
        return currentGoogleUser.reloadAuthResponse()
      })
      .then(function (newAuthResponse) {
        localStorage.setItem(ID_TOKEN_KEY, newAuthResponse.id_token)
        localStorage.setItem(JWT_REFRESH_COUNT_KEY, getJWTRefreshCount() + 1)
        console.log(`Refreshed expired JWT...`, getJWTRefreshCount() + 1)
      })
  })}

  function getJWTRefreshCount() {
    return parseInt((localStorage.getItem(JWT_REFRESH_COUNT_KEY) || '0'), 10)
  }

All 5 comments

Same question here.

Does anyone find a way to get refresh_token?

Had the same issue with the JWT token expiring and logging the user out. I cooked up a scheme to detect JWT expiration and refresh it when it has expired. You can configure longer logged in durations by increasing MAX_JWT_REFRESHES:

<head>
  <script src="https://apis.google.com/js/platform.js" async defer></script>
</head>
// in your app layout component or other component which is guaranteed to be available
export default class AppLayout extends Component {
  componentDidMount() {
    startJWTRefreshChecker()
  }

  render() {
    // ...
  }
}
import decode from 'jwt-decode'
import Promise from 'bluebird'

import AppConfig from 'config'
import { ID_TOKEN_KEY } from 'helpers/authHelper'

const MAX_JWT_REFRESHES = 10
const JWT_REFRESH_COUNT_KEY = 'jwt_refresh_count'
export const CHECK_JWT_REFRESH_TIMER_KEY = 'check_jwt_refresh_timer'

export function getJWTExpirationDate(token) {
  const decoded = decode(token)
  const date = new Date(0) // The 0 here is the key, which sets the date to the epoch

  if(!decoded.exp) {
    return null
  }

  date.setUTCSeconds(decoded.exp)

  return date
}

export function checkJWTRefresh() {
  const token = localStorage.getItem(ID_TOKEN_KEY)

  console.log(`checkJWTRefresh...`)

  if (!shouldRefreshJWT()) {
    console.log('Maxed out JWT refreshes...')
    return stopJWTRefreshChecker()
  }

  if (isJWTExpired(token)) {
    refreshExpiredJWT()
  }
}

export function shouldRefreshJWT() {
  return getJWTRefreshCount() < MAX_JWT_REFRESHES
}

export function startJWTRefreshChecker() {
  // start the refresh checker (checking once / sec)
  return localStorage.setItem(CHECK_JWT_REFRESH_TIMER_KEY, setInterval(checkJWTRefresh, 1000))
}

export function stopJWTRefreshChecker() {
  console.log('Disabling refresh check...')

  return clearInterval(localStorage.getItem(CHECK_JWT_REFRESH_TIMER_KEY))
}

export function resetJWTRefreshCount() {
  console.log('Resetting refresh count...')

  return localStorage.setItem(JWT_REFRESH_COUNT_KEY, 0)
}

export function isJWTExpired(token) {
  const date = getJWTExpirationDate(token)

  if (date === null) {
    return false
  }

  return !(date.valueOf() > new Date().valueOf())
}

export function refreshExpiredJWT() {
  window.gapi.load('auth2', function () {
    const existingAuthInstance = window.gapi.auth2.getAuthInstance()

    let currentUserPromise

    if (existingAuthInstance) {
      currentUserPromise = Promise.resolve(existingAuthInstance.currentUser.get())
    } else {
      currentUserPromise = window.gapi.auth2.init({
        client_id: AppConfig.googleClientId,
        scope: AppConfig.googleClientScopes
      }).then(function (res) {
        return res.currentUser.get()
      })
    }

    return currentUserPromise
      .then((currentGoogleUser) => {
        return currentGoogleUser.reloadAuthResponse()
      })
      .then(function (newAuthResponse) {
        localStorage.setItem(ID_TOKEN_KEY, newAuthResponse.id_token)
        localStorage.setItem(JWT_REFRESH_COUNT_KEY, getJWTRefreshCount() + 1)
        console.log(`Refreshed expired JWT...`, getJWTRefreshCount() + 1)
      })
  })}

  function getJWTRefreshCount() {
    return parseInt((localStorage.getItem(JWT_REFRESH_COUNT_KEY) || '0'), 10)
  }

I figured it out after lots of trial and error. You want to have the person authorize with responseType="code" on the client, then take the returned code and turn it into an access token and refresh token by using the call here on your server:
https://developers.google.com/identity/protocols/OAuth2WebServer#exchange-authorization-code

I have question like scandinaro commented. I get google reponse after login by email successfully and I call function reloadAuthResponse(), it is working properly , but I store them in redux and call again, it is not working! Give me a explain, please! Thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rishipr picture rishipr  路  7Comments

deitas picture deitas  路  8Comments

scolyer picture scolyer  路  3Comments

pulkit-max picture pulkit-max  路  8Comments

anthonyjgrove picture anthonyjgrove  路  8Comments