You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
+++
slug = "auth0-google-access-token"
reading_time = "10m"
summary = "A guide to retrieve Google access tokens from Auth0 in the frontend"
+++
After moving from Google implicit grant authorization to code model, I saw there were a bunch of things that were still left to do when it comes to user management. I want to be able to control which users log in, monitor problems properly, etc.
This is when I decided to explore Auth0 which offers a decent free plan.
🔧 Initial setup
Initial configuration with Auth0 is really smooth, their docs are amazing and it just felt awesome compared to integrating directly with Google Oauth using their docs.
In our case, we are going with client side auth and thus, we are choosing the path of using @auth0/auth0-react. Once you've created your Social login with Google/Auth0, you can set your provider in your root as follows:
This works easily, first attempt which is a lot of saying from where we are coming from. When you log in, it creates the user in Auth0 with all the information you expect it to. You can inspect the user profile and raw information and see it has pulled the information from your Google profile.
🤷 Where is my access token?
One of the things I quickly realized was that I didn't see any access_token in my user profile. After searching a bit, you discover that the token is indeed there but you have to retrieve it through the management API as this information is found under identities object and you need the read:user_idp_tokens scope.
What's most annoying is that there is no official supported way to retrieve IDP access tokens in your frontend. Their docs tell you to create a backend which.. I don't want to so I decided to hack my way through this.
🥷 The hack
I don't understand why it's so complicated to have access to a Google access token when, if you integrate directly with Google Oauth2, the access token is readily available and accessed from the frontend.
After playing a lot with the different configuration options from Auth0, I managed to implement a workaround that allows me to retrieve it in the frontend with no backend.
🤯 Custom social provider
The first step is to find a way to expose the access_token somewhere that can be then accessed by standard ways for Auth0 to retrieve custom data. The only way I found to do this is by writing a custom social provider.
The reason is because the "Fetch User Profile Script" has access to the access token returned by the social provider after logging in. To go with this step then, you just need to create a new custom social provider with the same exact parameters except the profile script which should be:
function(accessToken,ctx,cb){constinfo=JSON.parse(atob(ctx.id_token.split('.')[1]));constprofile={user_id: info.sub,email: info.email,name: info.name,picture: info.picture,email_verified: info.email_verified,user_metadata: {access_token: accessToken,},};// Call OAuth2 API with the accessToken and create the profilecb(null,profile);}
Basically, what we are doing here is set the accessToken received by the function as user_metadata. This accessToken is the Social provider one, not the Auth0 one!
Once you've done this, if you test this connection, you will see that the access_token attribute is filled in the user metadata.
Note that, in order to use the new connection, you have to specify it in the Auth0Provider:
which is quite self explanatory. It means, add a new attribute accessToken to the id_token with the value of user_metadata.access_token. In the frontend you will be able to then do:
const{ user }=useAuth0();console.log(user.accessToken);
🍎 Via user management API
I don't recommend this approach but leaving it here for documentation purposes in case someone (or me) needs to do some more complicated stuff using the management API from the frontend.
Next step is to access user metadata in the frontend. To do so, we need to issue a request to user management API. We can issue these requests using the access token that Auth0 returns as described in their docs. As mentioned, the requests sent from the frontend using this token have very limited scope (i.e. read current user, modify metadata, etc).
First, need to modify the provider again by adding the audience (the management API) and the scope so we can read the user:
Next, we need to retrieve the Auth0 token and with it, retrieve the google access token:
const{ user, isAuthenticated, getAccessTokenSilently }=useAuth0();const[accessToken,setAccessToken]=React.useState('');/** * If we have an authenticated user, retrieve the Google access token * using the management API from Auth0. Note we are super hacky here as * we store the access token from Google in the user metadata of Auth0 * so we can access it. */React.useEffect(()=>{asyncfunctionload(u: User){constmanagementToken=awaitgetAccessTokenSilently(); # ThisreturnstheAuth0access_tokenconstuserDetailsByIdUrl=`https://<your_auth0_domain>/api/v2/users/${u.sub}`;constmetadataResponse=awaitfetch(userDetailsByIdUrl,{headers: {Authorization: `Bearer ${managementToken}`,},});constresp=awaitmetadataResponse.json();setAccessToken(resp.user_metadata.access_token); # ThisisanaccesstokenthatcanbeusedbyGAPI}if(user&&isAuthenticated){load(user);}},[user,isAuthenticated,getAccessTokenSilently,setAccessToken]);
And that's it, once this is done, you can set the gapi access token to what the hook sets in accessToken and queries will work
for whatever scopes you requested when authorizing using the custom provider.
😵 Refresh
Access tokens emitted by Google Oauth only last for 1 hour which means, after one hour, your app will break when trying to access those resources. In order to keep the app working, you need to refresh the access_token using a refresh_token. Refresh tokens are returned by Google when you pass access_type as offline when authorizing. However, we can't repeat the same approach as with the access_token because the refresh token is not available in the "Fetch User Profile Script".
Currently, as documented by Auth0 docs it's not possible to refresh tokens without a backend and I couldn't find a workaround as I did with access tokens. For this case, what I decided to do is make the session with Auth0 to be of one hour so users need to sign in again after one hour so the IDP access token is refreshed.
💚 Conclusion
I don't understand why it's so difficult to retrieve IDP access tokens from the frontend when direct integrations with the social providers do just that, return an access token that you can just use.
Anyway, below is a list of interesting resources/docs I used when implementing this
argaen
changed the title
[draft] Retrieving Google access tokens from Auth0 client side
Retrieving Google access tokens from Auth0 client side
Feb 7, 2024
+++
slug = "auth0-google-access-token"
reading_time = "10m"
summary = "A guide to retrieve Google access tokens from Auth0 in the frontend"
+++
After moving from Google implicit grant authorization to code model, I saw there were a bunch of things that were still left to do when it comes to user management. I want to be able to control which users log in, monitor problems properly, etc.
This is when I decided to explore Auth0 which offers a decent free plan.
🔧 Initial setup
Initial configuration with Auth0 is really smooth, their docs are amazing and it just felt awesome compared to integrating directly with Google Oauth using their docs.
In our case, we are going with client side auth and thus, we are choosing the path of using
@auth0/auth0-react
. Once you've created your Social login with Google/Auth0, you can set your provider in your root as follows:This works easily, first attempt which is a lot of saying from where we are coming from. When you log in, it creates the user in Auth0 with all the information you expect it to. You can inspect the user profile and raw information and see it has pulled the information from your Google profile.
🤷 Where is my access token?
One of the things I quickly realized was that I didn't see any
access_token
in my user profile. After searching a bit, you discover that the token is indeed there but you have to retrieve it through the management API as this information is found underidentities
object and you need theread:user_idp_tokens
scope.What's most annoying is that there is no official supported way to retrieve IDP access tokens in your frontend. Their docs tell you to create a backend which.. I don't want to so I decided to hack my way through this.
🥷 The hack
I don't understand why it's so complicated to have access to a Google access token when, if you integrate directly with Google Oauth2, the access token is readily available and accessed from the frontend.
After playing a lot with the different configuration options from Auth0, I managed to implement a workaround that allows me to retrieve it in the frontend with no backend.
🤯 Custom social provider
The first step is to find a way to expose the
access_token
somewhere that can be then accessed by standard ways for Auth0 to retrieve custom data. The only way I found to do this is by writing a custom social provider.The reason is because the "Fetch User Profile Script" has access to the access token returned by the social provider after logging in. To go with this step then, you just need to create a new custom social provider with the same exact parameters except the profile script which should be:
Basically, what we are doing here is set the
accessToken
received by the function asuser_metadata
. ThisaccessToken
is the Social provider one, not the Auth0 one!Once you've done this, if you test this connection, you will see that the
access_token
attribute is filled in the user metadata.Note that, in order to use the new connection, you have to specify it in the
Auth0Provider
:🔌 Exposing user metadata in the frontend
🍏 Via custom claims
This is a documented approach for when you want to expose custom attributes in the User object that Auth0 returns.
It is the simplest and what I would recommend as it consists only on adding a custom Action to the login flow with the following code:
which is quite self explanatory. It means, add a new attribute
accessToken
to theid_token
with the value ofuser_metadata.access_token
. In the frontend you will be able to then do:🍎 Via user management API
Next step is to access user metadata in the frontend. To do so, we need to issue a request to
user management
API. We can issue these requests using the access token that Auth0 returns as described in their docs. As mentioned, the requests sent from the frontend using this token have very limited scope (i.e. read current user, modify metadata, etc).First, need to modify the provider again by adding the audience (the management API) and the scope so we can read the user:
Next, we need to retrieve the Auth0 token and with it, retrieve the google access token:
And that's it, once this is done, you can set the gapi access token to what the hook sets in
accessToken
and queries will workfor whatever scopes you requested when authorizing using the custom provider.
😵 Refresh
Access tokens emitted by Google Oauth only last for 1 hour which means, after one hour, your app will break when trying to access those resources. In order to keep the app working, you need to refresh the
access_token
using arefresh_token
. Refresh tokens are returned by Google when you passaccess_type
asoffline
when authorizing. However, we can't repeat the same approach as with theaccess_token
because the refresh token is not available in the "Fetch User Profile Script".Currently, as documented by Auth0 docs it's not possible to refresh tokens without a backend and I couldn't find a workaround as I did with access tokens. For this case, what I decided to do is make the session with Auth0 to be of one hour so users need to sign in again after one hour so the IDP access token is refreshed.
💚 Conclusion
I don't understand why it's so difficult to retrieve IDP access tokens from the frontend when direct integrations with the social providers do just that, return an access token that you can just use.
Anyway, below is a list of interesting resources/docs I used when implementing this
And as always, here you can find the PR with all the changes for shipping this to Maffin.
The text was updated successfully, but these errors were encountered: