How can I decode a google OAuth 2.0 JWT / credential token?
Asked Answered
B

3

9

I'm building a browser app that requires to authenticate with Google using the OAuth 2.0 / JWT workflow outlined in the link.

In the scenario of success user authentication with Google OAuth 2.0, Google API sends to an app OAuth the response like this:

{
  "clientId": "xxx...apps.googleusercontent.com",
  "credential": "yyy...123...zzz",
  "select_by": "user"
}

I have a client_id and using the NodeJS + JS.

How can I provide the app with the real user data once the user is authenticated?

Bleacher answered 26/7, 2021 at 4:1 Comment(0)
B
9

After some forth and back attempts it got clear that standard import jwt from 'jsonwebtoken' does not work and Google uses its own encoding npm library - google-auth-library, see more here. The basic solution is the following:

const { OAuth2Client } = require('google-auth-library')

/**
 * @description Function to decode Google OAuth token
 * @param token: string
 * @returns ticket object
 */
export const getDecodedOAuthJwtGoogle = async token => {

  const CLIENT_ID_GOOGLE = 'yourGoogleClientId'

  try {
    const client = new OAuth2Client(CLIENT_ID_GOOGLE)

    const ticket = await client.verifyIdToken({
      idToken: token,
      audience: CLIENT_ID_GOOGLE,
    })

    return ticket
  } catch (error) {
    return { status: 500, data: error }
  }
}

Usage:

const realUserData = getDecodedOAuthJwtGoogle(credential) // credentials === JWT token

If your token (credential) is valid then realUserData will hopefully have a value like this:

{
  // These six fields are included in all Google ID Tokens.
  "iss": "https://accounts.google.com",
  "sub": "110169484474386276334",
  "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
  "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
  "iat": "1433978353",
  "exp": "1433981953",

  // These seven fields are only included when the user has granted the "profile" and
  // "email" OAuth scopes to the application.
  "email": "[email protected]",
  "email_verified": "true",
  "name" : "Test User",
  "picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
  "given_name": "Test",
  "family_name": "User",
  "locale": "en"
}
Bleacher answered 26/7, 2021 at 4:1 Comment(1)
Nice, but how I integrate this solution into a javascript based web app that does not work with npm? I need a native javascript based solutionCatabasis
A
5

You can use jwt-decode library to decode your credential OAuth Login, here is the step by step how to use it

  1. Install jwt-decode

You can use npm install jwt-decode or yarn add jwt-decode or pnpm install jwt-decode

  1. Import jwt-decode to your App

import { GoogleLogin } from '@react-oauth/google';
import jwtDecode from 'jwt-decode'

...

<GoogleLogin 
 onSuccess = {credentialResponse => {
   if (credentialResponse.credential != null) {
    const USER_CREDENTIAL = jwtDecode(credentialResponse.credential);
    console.log(USER_CREDENTIAL);
   }
  }
 }
...

I use GoogleLoginOAuth from @react-oauth/google, at my case this code it's work!

One more thing, ** just intermezzo ** if you use TypeScript, then you want destructure object to get name or family_name for instance

const { given_name, family_name } = USER_CREDENTIAL;

and then you get this error squiggles like this

Property 'family_name' does not exist on type 'unknown'

You can make type like this, copy it if you need it anyway

dataCredential.ts

export type dataCredential = {
    aud: string,
    azp: string,
    email: string,
    email_verified: boolean,
    exp: number,
    family_name: string,
    given_name: string,
    iss: string,
    jti: string,
    name: string,
    nbf: number,
    picture: string,
    sub: string
}

and than you can make your code like this


import { GoogleLogin } from '@react-oauth/google';
import jwtDecode from 'jwt-decode'
import {dataCredential} from "../types/dataCredential";

...

<GoogleLogin 
 onSuccess = {credentialResponse => {
   if (credentialResponse.credential != null) {
    const USER_CREDENTIAL: dataCredential = jwtDecode(credentialResponse.credential);
    console.log(USER_CREDENTIAL);
    const { given_name, family_name } = USER_CREDENTIAL;
    console.log(given_name, family_name)
   }
  }
 }
...

Hope it helps!

Autocorrelation answered 18/1, 2023 at 1:53 Comment(0)
A
2

I was stuck here too, but I found a solution here.

Google's example:

See Google

<div id="g_id_onload"
     data-client_id="YOUR_GOOGLE_CLIENT_ID"
     data-callback="handleCredentialResponse">
</div>
<script>
  function handleCredentialResponse(response) {
     // decodeJwtResponse() is a custom function defined by you
     // to decode the credential response.
     const responsePayload = decodeJwtResponse(response.credential);

     console.log("ID: " + responsePayload.sub);
     console.log('Full Name: ' + responsePayload.name);
     console.log('Given Name: ' + responsePayload.given_name);
     console.log('Family Name: ' + responsePayload.family_name);
     console.log("Image URL: " + responsePayload.picture);
     console.log("Email: " + responsePayload.email);
  }
</script>

Here's the function that worked for me:

I have only adapted the function name, which was provided by @Peheje in the other thread, so that this works with the code example from Google

function decodeJwtResponse(token) {
  var base64Url = token.split('.')[1];
  var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
  var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
    return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
  }).join(''));

  return JSON.parse(jsonPayload);
}
Almandine answered 7/10, 2023 at 12:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.