Google Identity for server-side web app - redirect URI mismatch - google-api

I'm attempting to set up the Code Model for Google authentication, so that my user can oauth with Google and my app can retrieve their Calendar data. I'm stuck on step 5 here, where I'm supposed to exchange the authorization code for refresh and access tokens. I'm using nestjs in the backend and React in the frontend.
What I've done already that's working:
User clicks a button on my web app's page
Client sets up google.accounts.oauth2.initCodeClient with the /calendar scope, in ux_mode: popup
User is shown the Google popup and can auth thru that
Client receives a response from Google containing the authorization code
Client makes a POST call to my backend to send it just that authorization code
In step 5, the client makes the POST call to localhost:4000/auth/google-test. In the backend, I'm using the googleapis package and have:
export const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
'http://localhost:4000/' // <- note, I'm not sure if this is corect
);
And in the relevant controller route, I'm doing:
#Post('google-test')
public async googleTest(#Body() bodyReceived: any): Promise<any> {
let { code } = bodyReceived
let { tokens } = await oauth2Client.getToken(code)
oauth2Client.setCredentials(tokens);
console.log('Tokens: ' + tokens);
return
The error I'm getting is related to oauth2Client.getToken(code), and the error is a redirect_uri_mismatch. In GCP for the credentials for this app, I've added all of these as "Authorized redirect URIs":
http://localhost:3000/home
http://localhost:4000/auth/google-test
http://localhost:4000
What am I doing wrong?

It took a bit more Googling, but turns out that the right answer is to have my server make the token call with the redirect uri as "postmessage".
This SO question gives a bit more context. A somewhat unbelievable message, but it seems to work for my app.

It is evidently that what is happening is that the redirect URI does not match with the one in the GCP. This usually happens because backend tools such as Nestjs may be appending a trailing '/' to the URL and it may be interpreted as being part of the redirect_uri value.
You can try by temoving any trailing '/' via this following method oauthurl.replace(/\/$/, '')
Moreover, you can pass the generated auth URL to a meta tag. And check the html header to confirm what is the URL value.

Related

How to silently renew Id Token using AddMicrosoftIdentityWebAppAuthentication to Call Downstream API

I am trying to implement the BFF-Gateway pattern (no tokens in the browser) to be used with a React SPA. The BFF is using AddMicrosoftIdentityWebAppAuthentication to handle login and issue a cookie to the SPA. And it is using YARP to proxy api requests to a downstream api. I'm using Azure B2C. Everything works perfectly until the BFF id_token expires in 1 hour. At that point, fetching the downstream api access token via GetAccessTokenForUserAsync (which is called in a piece of middleware) fails:
var scope = _configuration["CallApi:ScopeForAccessToken"];
var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { scope });
ctx.Request.Headers.Add("Authorization", "Bearer " + accessToken);
Exception:
IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.
ResponseBody: {"error":"invalid_grant","error_description":"AADB2C90085: The service has encountered an internal error. Please reauthenticate and try again.\r\nCorrelation ID: 622d6bd6-d06e-4142-86f2-b30a7a17b3b5\r\nTimestamp: 2022-11-25 09:31:23Z\r\n"}
This is effectively the same as Call Downstream API Without The Helper Class example and this sample, except that I'm acquiring the access token in middleware, not a controller, so the downstream YARP requests contain the access token. BTW I get the same error if I do this inside a controller per this example. And I see no soluton to this in the sample.
There is a similar question here which references the sample referenced above, but for the B2C sample I see no solution to this problem.
I also found this sample and this explanation. But this uses Microsoft.Owin to configure auth, not AddMicrosoftIdentityWebAppAuthentication. This looks promising, but is a departure from most examples I see that use Microsoft.Identity.Web.
Can you please point to the correct soluton? I need call to be able to call _tokenAcquisition.GetAccessTokenForUserAsync after the id token expires without asking the user to reauthenticate and/or the SPA to having to reload.
At the moment I am handling this issue in the SPA by catching the exception from MSAL and redirecting back to the login endpoint in the BFF which initiates the challenge. This gets me a new id_token and cookie, but this is just a temp workaround as it's very disruptive to user to be redirected away from the SPA.

.Net MAUI WebAuthenticator CallbackUrl for social logins

I'm following this guide to setup authenticating with social logins using MSAL and Web Authenticator. The example call looks like this:
WebAuthenticatorResult authResult = await WebAuthenticator.Default.AuthenticateAsync(
new Uri("https://example.com/mobileauth/Microsoft"),
new Uri("myapp://"));
But what should the second URI parameter be? In the guide it says:
The URI the flow is expected to ultimately call back to, that is registered to your app.
So how do I register a URI like that to my app?? I've tried following this guide and have 'registered' my app in azure active directory but facebook/google etc won't accept urls of the form "myapp://" as valid redirect URIs... What am I missing?
Update
So the first half of the flow is working but I keep getting errors from the OAuth providers (the green highlight in the diagram isn't working).
This from Google:
And this from Facebook:
But I've added all these valid callback URLs:
I'm afraid that example is broken.
The second URI parameter represents where your app is listening to get the authentication result.
Actually, myapp:// is not a valid URI. You should try with something like myapp://callback.
Finally got to the bottom of it thanks to this old Xamarin issue: https://github.com/xamarin/Essentials/issues/1224#issuecomment-618192336
You have to set the "CallbackPath" property in the API config like so:
.AddGoogle(g => {
g.ClientId = "{Client ID}";
g.ClientSecret = "{Client Secret}";
g.CallbackPath = "/mobileauth"; //mobileauth is api controller
g.SaveTokens = true; })
And then tell the provider of that redirect e.g. adding "https://{API-URL}/mobileauth" in google console.

Can't authenticate google app to read email [duplicate]

On the website https://code.google.com/apis/console I have registered my application, set up generated Client ID: and Client Secret to my app and tried to log in with Google.
Unfortunately, I got the error message:
Error: redirect_uri_mismatch
The redirect URI in the request: http://127.0.0.1:3000/auth/google_oauth2/callback did not match a registered redirect URI
scope=https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
response_type=code
redirect_uri=http://127.0.0.1:3000/auth/google_oauth2/callback
access_type=offline
approval_prompt=force
client_id=generated_id
What does mean this message, and how can I fix it?
I use the gem omniauth-google-oauth2.
The redirect URI (where the response is returned to) has to be registered in the APIs console, and the error is indicating that you haven't done that, or haven't done it correctly.
Go to the console for your project and look under API Access. You should see your client ID & client secret there, along with a list of redirect URIs. If the URI you want isn't listed, click edit settings and add the URI to the list.
EDIT: (From a highly rated comment below) Note that updating the google api console and that change being present can take some time. Generally only a few minutes but sometimes it seems longer.
In my case it was www and non-www URL. Actual site had www URL and the Authorized Redirect URIs in Google Developer Console had non-www URL. Hence, there was mismatch in redirect URI. I solved it by updating Authorized Redirect URIs in Google Developer Console to www URL.
Other common URI mismatch are:
Using http:// in Authorized Redirect URIs and https:// as actual URL, or vice-versa
Using trailing slash (http://example.com/) in Authorized Redirect URIs and not using trailing slash (http://example.com) as actual URL, or vice-versa
Here are the step-by-step screenshots of Google Developer Console so that it would be helpful for those who are getting it difficult to locate the developer console page to update redirect URIs.
Go to https://console.developers.google.com
Select your Project
Click on the menu icon
Click on API Manager menu
Click on Credentials menu. And under OAuth 2.0 Client IDs, you will find your client name. In my case, it is Web Client 1. Click on it and a popup will appear where you can edit Authorized Javascript Origin and Authorized redirect URIs.
Note: The Authorized URI includes all localhost links by default, and any live version needs to include the full path, not just the domain, e.g. https://example.com/path/to/oauth/url
Here is a Google article on creating project and client ID.
If you're using Google+ javascript button, then you have to use postmessage instead of the actual URI. It took me almost the whole day to figure this out since Google's docs do not clearly state it for some reason.
In any flow where you retrieved an authorization code on the client side, such as the GoogleAuth.grantOfflineAccess() API, and now you want to pass the code to your server, redeem it, and store the access and refresh tokens, then you have to use the literal string postmessage instead of the redirect_uri.
For example, building on the snippet in the Ruby doc:
client_secrets = Google::APIClient::ClientSecrets.load('client_secrets.json')
auth_client = client_secrets.to_authorization
auth_client.update!(
:scope => 'profile https://www.googleapis.com/auth/drive.metadata.readonly',
:redirect_uri => 'postmessage' # <---- HERE
)
# Inject user's auth_code here:
auth_client.code = "4/lRCuOXzLMIzqrG4XU9RmWw8k1n3jvUgsI790Hk1s3FI"
tokens = auth_client.fetch_access_token!
# { "access_token"=>..., "expires_in"=>3587, "id_token"=>..., "refresh_token"=>..., "token_type"=>"Bearer"}
The only Google documentation to even mention postmessage is this old Google+ sign-in doc. Here's a screenshot and archive link since G+ is closing and this link will likely go away:
It is absolutely unforgivable that the doc page for Offline Access doesn't mention this. #FacePalm
For my web application i corrected my mistake by writing
instead of : http://localhost:11472/authorize/
type : http://localhost/authorize/
Make sure to check the protocol "http://" or "https://" as google checks protocol as well.
Better to add both URL in the list.
1.you would see an error like this
2.then you should click on request details
after this , you have to copy that url and add this on https://console.cloud.google.com/
go to https://console.cloud.google.com/
click on Menu -> API & Services -> Credentials
you would see a dashboard like this ,click on edit OAuth Client
now in Authorized Javascript Origins and Authorized redirect URLS
add the url that has shown error called redirect_uri_mismatch i.e here it is
http://algorithammer.herokuapp.com , so i have added that in both the places in
Authorized Javascript Origins and Authorized redirect URLS
click on save and wait for 5 min and then try to login again
This seems quite strange and annoying that no "one" solution is there.
for me http://localhost:8000 did not worked out but http://localhost:8000/ worked out.
This answer is same as this Mike's answer, and Jeff's answer, both sets redirect_uri to postmessage on client side. I want to add more about the server side, and also the special circumstance applying to this configuration.
Tech Stack
Backend
Python 3.6
Django 1.11
Django REST Framework 3.9: server as API, not rendering template, not doing much elsewhere.
Django REST Framework JWT 1.11
Django REST Social Auth < 2.1
Frontend
React: 16.8.3, create-react-app version 2.1.5
react-google-login: 5.0.2
The "Code" Flow (Specifically for Google OAuth2)
Summary: React --> request social auth "code" --> request jwt token to acquire "login" status in terms of your own backend server/database.
Frontend (React) uses a "Google sign in button" with responseType="code" to get an authorization code. (it's not token, not access token!)
The google sign in button is from react-google-login mentioned above.
Click on the button will bring up a popup window for user to select account. After user select one and the window closes, you'll get the code from the button's callback function.
Frontend send this to backend server's JWT endpoint.
POST request, with { "provider": "google-oauth2", "code": "your retrieved code here", "redirect_uri": "postmessage" }
For my Django server I use Django REST Framework JWT + Django REST Social Auth. Django receives the code from frontend, verify it with Google's service (done for you). Once verified, it'll send the JWT (the token) back to frontend. Frontend can now harvest the token and store it somewhere.
All of REST_SOCIAL_OAUTH_ABSOLUTE_REDIRECT_URI, REST_SOCIAL_DOMAIN_FROM_ORIGIN and REST_SOCIAL_OAUTH_REDIRECT_URI in Django's settings.py are unnecessary. (They are constants used by Django REST Social Auth) In short, you don't have to setup anything related to redirect url in Django. The "redirect_uri": "postmessage" in React frontend suffice. This makes sense because the social auth work you have to do on your side is all Ajax-style POST request in frontend, not submitting any form whatsoever, so actually no redirection occur by default. That's why the redirect url becomes useless if you're using the code + JWT flow, and the server-side redirect url setting is not taking any effect.
The Django REST Social Auth handles account creation. This means it'll check the google account email/last first name, and see if it match any account in database. If not, it'll create one for you, using the exact email & first last name. But, the username will be something like youremailprefix717e248c5b924d60 if your email is youremailprefix#example.com. It appends some random string to make a unique username. This is the default behavior, I believe you can customize it and feel free to dig into their documentation.
The frontend stores that token and when it has to perform CRUD to the backend server, especially create/delete/update, if you attach the token in your Authorization header and send request to backend, Django backend will now recognize that as a login, i.e. authenticated user. Of course, if your token expire, you have to refresh it by making another request.
Oh my goodness, I've spent more than 6 hours and finally got this right! I believe this is the 1st time I saw this postmessage thing. Anyone working on a Django + DRF + JWT + Social Auth + React combination will definitely crash into this. I can't believe none of the article out there mentions this except answers here. But I really hope this post can save you tons of time if you're using the Django + React stack.
In my case, my credential Application type is "Other". So I can't find Authorized redirect URIs in the credentials page. It seems appears in Application type:"Web application". But you can click the Download JSON button to get the client_secret.json file.
Open the json file, and you can find the parameter like this: "redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]. I choose to use http://localhost and it works fine for me.
When you register your app at https://code.google.com/apis/console and
make a Client ID, you get a chance to specify one or more redirect
URIs. The value of the redirect_uri parameter on your auth URI has to
match one of them exactly.
Checklist:
http or https?
& or &?
trailing slash(/) or open ?
(CMD/CTRL)+F, search for the exact match in the credential page. If
not found then search for the missing one.
Wait until google refreshes it. May happen in each half an hour if you
are changing frequently or it may stay in the pool. For my case it was almost half an hour to take effect.
for me it was because in the 'Authorized redirect URIs' list I've incorrectly put https://developers.google.com/oauthplayground/ instead of https://developers.google.com/oauthplayground (without / at the end).
The redirect url is case sensitive.
In my case I added both:
http://localhost:5023/AuthCallback/IndexAsync
http://localhost:5023/authcallback/indexasync
If you use this tutorial: https://developers.google.com/identity/sign-in/web/server-side-flow then you should use "postmessage".
In GO this fixed the problem:
confg = &oauth2.Config{
RedirectURL: "postmessage",
ClientID: ...,
ClientSecret: ...,
Scopes: ...,
Endpoint: google.Endpoint,
}
beware of the extra / at the end of the url
http://localhost:8000 is different from http://localhost:8000/
It has been answered thoroughly but recently (like, a month ago) Google stopped accepting my URI and it would not worked. I know for a fact it did before because there is a user registered with it.
Anyways, the problem was the regular 400: redirect_uri_mismatch but the only difference was that it was changing from https:// to http://, and Google will not allow you to register http:// redirect URI as they are production publishing status (as opposed to localhost).
The problem was in my callback (I use Passport for auth) and I only did
callbackURL: "/register/google/redirect"
Read docs and they used a full URL, so I changed it to
callbackURL: "https://" + process.env.MY_URL+ "/register/google/redirect"
Added https localhost to my accepted URI so I could test locally, and it started working again.
TL;DR use the full URL so you know where you're redirecting
2015 July 15 - the signin that was working last week with this script on login
<script src="https://apis.google.com/js/platform.js" async defer></script>
stopped working and started causing Error 400 with Error: redirect_uri_mismatch
and in the DETAILS section: redirect_uri=storagerelay://...
i solved it by changing to:
<script src="https://apis.google.com/js/client:platform.js?onload=startApp"></script>
Rails users (from the omniauth-google-oauth2 docs):
Fixing Protocol Mismatch for redirect_uri in Rails
Just set the full_host in OmniAuth based on the Rails.env.
# config/initializers/omniauth.rb
OmniAuth.config.full_host = Rails.env.production? ? 'https://domain.com' : 'http://localhost:3000'
REMEMBER: Do not include the trailing "/"
None of the above solutions worked for me. below did
change authorised Redirect urls to - https://localhost:44377/signin-google
Hope this helps someone.
My problem was that I had http://localhost:3000/ in the address bar and had http://127.0.0.1:3000/ in the console.developers.google.com
Just make sure that you are entering URL and not just a domain.
So instead of:
domain.com
it should be
domain.com/somePathWhereYouHadleYourRedirect
Anyone struggling to find where to set redirect urls in the new console: APIs & Auth -> Credentials -> OAuth 2.0 client IDs -> Click the link to find all your redirect urls
My two cents:
If using the Google_Client library do not forget to update the JSON file on your server after updating the redirect URI's.
I also get This error Error-400: redirect_uri_mismatch
This is not a server or Client side error but you have to only change by checking that you haven't to added / (forward slash) at the end like this
redirecting URL list ❌:
https://developers.google.com/oauthplayground/
Do this only ✅:
https://developers.google.com/oauthplayground
Let me complete #Bazyl's answer: in the message I received, they mentioned the URI
"http://localhost:8080/"
(which of course, seems an internal google configuration). I changed the authorized URI for that one,
"http://localhost:8080/" , and the message didn't appear anymore... And the video got uploaded... The APIS documentation is VERY lame... Every time I have something working with google apis, I simply feel "lucky", but there's a lack of good documentation about it.... :( Yes, I got it working, but I don't yet understand neither why it failed, nor why it worked... There was only ONE place to confirm the URI in the web, and it got copied in the client_secrets.json... I don't get if there's a THIRD place where one should write the same URI... I find nor only the documentation but also the GUI design of Google's api quite lame...
I needed to create a new client ID under APIs & Services -> Credentials -> Create credentials -> OAuth -> Other
Then I downloaded and used the client_secret.json with my command line program that is uploading to my youtube account. I was trying to use a Web App OAuth client ID which was giving me the redirect URI error in browser.
I have frontend app and backend api.
From my backend server I was testing by hitting google api and was facing this error. During my whole time I was wondering of why should I need to give redirect_uri as this is just the backend, for frontend it makes sense.
What I was doing was giving different redirect_uri (though valid) from server (assuming this is just placeholder, it just has only to be registered to google) but my frontend url that created token code was different. So when I was passing this code in my server side testing(for which redirect-uri was different), I was facing this error.
So don't do this mistake. Make sure your frontend redirect_uri is same as your server's as google use it to validate the authenticity.
The main reason for this issue will only come from chrome and chrome handles WWW and non www differently depending on how you entered your URL in the browsers and it searches from google and directly shows the results, so the redirection URL sent is different in a different case
Add all the possible combinations you can find the exact url sent from fiddler , the 400 error pop up will not give you the exact http and www infromation
Try to do these checks:
Bundle ID in console and in your application. I prefer set Bundle ID of application like this "org.peredovik.${PRODUCT_NAME:rfc1034identifier}"
Check if you added URL types at tab Info just type your Bundle ID in Identifier and URL Schemes, role set to Editor
In console at cloud.google.com "APIs & auth" -> "Consent screen" fill form about your application. "Product name" is required field.
Enjoy :)

Google Oauth Error: redirect_uri_mismatch

I'm trying to use google Oauth 2 to authenticate with google calendar API for a web server running on AWS EC2.
When I generated the credentials I selected 'OAuth Client ID' and then 'Web Application'. For the Authorised redirect URIs I have entered:
http://ec2-XX-XX-XX-XXX.eu-west-1.compute.amazonaws.com
(I've blanked out the IP of my EC2 instance). I have checked this is the correct URL that I want the callback to go to.
The link that is generated in the server logs is of the form:
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=XXXXXXXXXXXX-XXXXXXXXXXXXXX.apps.googleusercontent.com&redirect_uri=http://localhost:47258/Callback&response_type=code&scope=https://www.googleapis.com/auth/calendar.readonly
When I follow the link I get the error
'Error: redirect_uri_mismatch'.
I've read this SO question and have checked that I am using HTTP and there is no trialing '/'
I suspect that the URL generated should not have 'localhost' in it but I've reset the client_secret.json several times and each time I restart tomcat with the new client secret I still get a link with localhost but just over a different port.
Locally, I had selected Credentials type of 'other' previously and was not given an option for the Authorised redirect URI. I did try this for the EC2 instance but this won't give me the control I want over the redirect URI and sends the redirect over localhost.
Google throws redirect_uri_mismatch when the uri (including ports) supplied with the request doesn't match the one registered with the application.
Make sure you registered the Authorised redirect URIs and Authorised JavaScript origins on the web console correctly.
This is a sample configuration that works for me.
In case you are seeing this error while making API call from your server to get tokens.
Short Answer 👇 - What solved my problem
use string postmessage in place of actual redirectUri that you configured on cloud console.
Here is my initilization of OAuth2 client that worked for me.
// import {Auth, google} from 'googleapis`;
const clientID = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
oauthClient = new google.auth.OAuth2(clientID,clientSecret,'postmessage');
My Case
On the frontend, I am using react to prompt the user for login with google with the authentication-code flow. On success, this returns code in the payload that needs to be posted to the google API server to get token - Access Token, Refresh Token, ID Token etc.
I am using googleapis package on my server. Here is how I am retrieving user info from google
// import {Auth, google} from 'googleapis`;
const clientID = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
oauthClient = new google.auth.OAuth2(clientID,clientSecret,'postmessage');
/*
get tokens from google to make api calls on behalf of user.
#param: code -> code posted to backend from the frontend after the user successfully grant access from consent screen
*/
const handleGoogleAuth = (code: string) => {
oauthClient.getToken(code, async (err, tokens: Auth.Credentials) {
if (err) throw new Error()
// get user information
const tokenInfo = await oauthClient.verifyIdToken({
idToken: tokens.id_token
});
const {email, given_name, family_name, email} = tokenInfo.getPayload();
// do whatever you want to do with user informaton
}
}
When creating a Oath client ID, DO NOT select web application, Select "Other". This way, the Redirect URI is not required.

How do I authorise an app (web or installed) without user intervention?

Let's say that I have a web app ("mydriveapp") that needs to access Drive files in a background service. It will either own the files it is accessing, or be run in a Google Account with which the owner has shared the documents.
I understand that my app needs a refresh token, but I don't want to write the code to obtain that since I'll only ever do it once.
NB. This is NOT using a Service Account. The app will be run under a conventional Google account. Service Account is a valid approach in some situations. However the technique of using Oauth Playground to simulate the app can save a bunch of redundant effort, and applies to any APIs for which sharing to a Service Account is unsupported.
NB June 2022. It seems that Google have updated their verification requirements which adds additional steps (or negates the approach - depending on your point of view).
See recent comments for more detail
This can be done with the Oauth2 Playground at https://developers.google.com/oauthplayground
Steps:-
Create the Google Account (eg. my.drive.app#gmail.com) - Or skip this step if you are using an existing account.
Use the API console to register the mydriveapp (https://console.developers.google.com/apis/credentials/oauthclient?project=mydriveapp or just https://console.developers.google.com/apis/)
Create a new set of credentials. Credentials/Create Credentials/OAuth Client Id then select Web application
Include https://developers.google.com/oauthplayground as a valid redirect URI
Note the client ID (web app) and Client Secret
Login as my.drive.app#gmail.com
Go to Oauth2 playground
In Settings (gear icon), set
OAuth flow: Server-side
Access type: Offline
Use your own OAuth credentials: TICK
Client Id and Client Secret: from step 5
Click Step 1 and choose Drive API v3 https://www.googleapis.com/auth/drive (having said that, this technique also works for any of the Google APIs listed)
Click Authorize APIs. You will be prompted to choose your Google account and confirm access
Click Step 2 and "Exchange authorization code for tokens"
Copy the returned Refresh token and paste it into your app, source code or in to some form of storage from where your app can retrieve it.
Your app can now run unattended, and use the Refresh Token as described https://developers.google.com/accounts/docs/OAuth2WebServer#offline to obtain an Access Token.
NB. Be aware that the refresh token can be expired by Google which will mean that you need to repeat steps 5 onwards to get a new refresh token. The symptom of this will be a Invalid Grant returned when you try to use the refresh token.
NB2. This technique works well if you want a web app which access your own (and only your own) Drive account, without bothering to write the authorization code which would only ever be run once. Just skip step 1, and replace "my.drive.app" with your own email address in step 6. make sure you are aware of the security implications if the Refresh Token gets stolen.
See Woody's comment below where he links to this Google video https://www.youtube.com/watch?v=hfWe1gPCnzc
.
.
.
Here is a quick JavaScript routine that shows how to use the Refresh Token from the OAuth Playground to list some Drive files. You can simply copy-paste it into Chrome dev console, or run it with node. Of course provide your own credentials (the ones below are all fake).
function get_access_token_using_saved_refresh_token() {
// from the oauth playground
const refresh_token = "1/0PvMAoF9GaJFqbNsLZQg-f9NXEljQclmRP4Gwfdo_0";
// from the API console
const client_id = "559798723558-amtjh114mvtpiqis80lkl3kdo4gfm5k.apps.googleusercontent.com";
// from the API console
const client_secret = "WnGC6KJ91H40mg6H9r1eF9L";
// from https://developers.google.com/identity/protocols/OAuth2WebServer#offline
const refresh_url = "https://www.googleapis.com/oauth2/v4/token";
const post_body = `grant_type=refresh_token&client_id=${encodeURIComponent(client_id)}&client_secret=${encodeURIComponent(client_secret)}&refresh_token=${encodeURIComponent(refresh_token)}`;
let refresh_request = {
body: post_body,
method: "POST",
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
})
}
// post to the refresh endpoint, parse the json response and use the access token to call files.list
fetch(refresh_url, refresh_request).then( response => {
return(response.json());
}).then( response_json => {
console.log(response_json);
files_list(response_json.access_token);
});
}
// a quick and dirty function to list some Drive files using the newly acquired access token
function files_list (access_token) {
const drive_url = "https://www.googleapis.com/drive/v3/files";
let drive_request = {
method: "GET",
headers: new Headers({
Authorization: "Bearer "+access_token
})
}
fetch(drive_url, drive_request).then( response => {
return(response.json());
}).then( list => {
console.log("Found a file called "+list.files[0].name);
});
}
get_access_token_using_saved_refresh_token();
Warning May 2022 - this answer may not be valid any longer - see David Stein's comment
Let me add an alternative route to pinoyyid's excellent answer (which didn't work for me - popping redirect errors).
Instead of using the OAuthPlayground you can also use the HTTP REST API directly. So the difference to pinoyyid's answer is that we'll do things locally. Follow steps 1-3 from pinoyyid's answer. I'll quote them:
Create the Google Account (eg. my.drive.app#gmail.com) - Or skip this step if you are using an existing account.
Use the API console to register the mydriveapp (https://console.developers.google.com/apis/credentials/oauthclient?project=mydriveapp or just https://console.developers.google.com/apis/)
Create a new set of credentials (NB OAuth Client ID not Service Account Key and then choose "Web Application" from the selection)
Now, instead of the playground, add the following to your credentials:
Authorized JavaScript Sources: http://localhost (I don't know if this is required but just do it.)
Authorized Redirect URIs: http://localhost:8080
Screenshot (in German):
Make sure to actually save your changes via the blue button below!
Now you'll probably want to use a GUI to build your HTTP requests. I used Insomnia but you can go with Postman or plain cURL. I recommend Insomnia for it allows you to go through the consent screens easily.
Build a new GET request with the following parameters:
URL: https://accounts.google.com/o/oauth2/v2/auth
Query Param: redirect_uri=http://localhost:8080
Query Param: prompt=consent
Query Param: response_type=code
Query Param: client_id=<your client id from OAuth credentials>
Query Param: scope=<your chosen scopes, e.g. https://www.googleapis.com/auth/drive.file>
Query Param: access_type=offline
If your tool of choice doesn't handle URL encoding automagically make sure to get it right yourself.
Before you fire your request set up a webserver to listen on http://localhost:8080. If you have node and npm installed run npm i express, then create an index.js:
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('ok');
console.log(req)
});
app.listen(8080, function () {
console.log('Listening on port 8080!');
});
And run the server via node index.js. I recommend to either not log the whole req object or to run node index.js | less for the full output will be huge.
There are very simple solutions for other languages, too. E.g. use PHP's built in web server on 8080 php -S localhost:8080.
Now fire your request (in Insomnia) and you should be prompted with the login:
Log in with your email and password and confirm the consent screen (should contain your chosen scopes).
Go back to your terminal and check the output. If you logged the whole thing scroll down (e.g. pgdown in less) until you see a line with code=4/....
Copy that code; it is your authorization code that you'll want to exchange for an access and refresh token. Don't copy too much - if there's an ampersand & do not copy it or anything after. & delimits query parameters. We just want the code.
Now set up a HTTP POST request pointing to https://www.googleapis.com/oauth2/v4/token as form URL encoded. In Insomnia you can just click that - in other tools you might have to set the header yourself to Content-Type: application/x-www-form-urlencoded.
Add the following parameters:
code=<the authorization code from the last step>
client_id=<your client ID again>
client_secret=<your client secret from the OAuth credentials>
redirect_uri=http://localhost:8080
grant_type=authorization_code
Again, make sure that the encoding is correct.
Fire your request and check the output from your server. In the response you should see a JSON object:
{
"access_token": "xxxx",
"expires_in": 3600,
"refresh_token": "1/xxxx",
"scope": "https://www.googleapis.com/auth/drive.file",
"token_type": "Bearer"
}
You can use the access_token right away but it'll only be valid for one hour. Note the refresh token. This is the one you can always* exchange for a new access token.
* You will have to repeat the procedure if the user changes his password, revokes access, is inactive for 6 months etc.
Happy OAuthing!

Resources