How to refresh Google Service account which expires after 1 hour? - oauth-2.0

I am using Google Drive API(C#) with service account as mentioned in
https://developers.google.com/drive/delegation
I am able to work with DriveService object, but after 1 hr, it errors out with exception: "The remote server returned an error: (401) Unauthorized."
I know, by setting "access_type" to "offline" we could solve this problem, but I am not able to set this property for DriveService object.
Does anyone know how to refresh this Google Drive Service object?
Thanks in advance

Service accounts come with a private key - and that's their moral equivalent/superset of the refresh token that is returned as a result of a user-driven consent flow.
When a user consents to offline access (via a web server or similar OAuth flow) a refresh token is returned that can be swapped (along with the client secret) at any time for an access token.
In the same manner a service account private key can be used to sign an assertion that can also be swapped for an access token - that's useful for cases where no user is present to accept a consent screen, or where you are performing work on behalf of other users in your organization.
Once you get an access token it is treated in the same way - and is expected to expire after 1 hour, at which time a new access token will need to be requested, which for a service account means creating and signing a new assertion.
Generally noticing that the access token is expired and requesting a new one is taken care of for you by the Google client libraries - although I'm not familiar with the C# version. If you could share your code that creates the DriveService object that would be helpful.

When you set offline access mode, your app gets a refresh token when the user logs in for the first time.
access_type ::
Indicates if your application needs to access a Google API when the
user is not present at the browser. This parameter defaults to online.
If your application needs to refresh access tokens when the user is
not present at the browser, then use offline. This will result in your
application obtaining a refresh token the first time your application
exchanges an authorization code for a user.
You later use this refresh token to obtain a new access token, once the current access token expires. Basically, your app would then hit the token exchange endpoint (POST to https://accounts.google.com/o/oauth2/token) with the refresh token and your client credentials - google with then issue a (refresh token + access token) pair to you.
See this link for further clarification.
EDIT - I checked the Service Account documentation and found a sample C# app that fetches and uses refresh tokens too. See it here.I hope this one helps.

Related

JWT: what is the advantage of a refresh token when using grant_type=password

I'm following this article to understand refesh tokens.
In my case I have to connect to REST api using grant_type=password, and I receive a token with a 5 minute lifespan. So every 5 minutes I have to issue a POST request passing client-id, username & password to get a new acces token.
The other option would be to issue a POST with a grant_type=refresh_token, without having to send the username & password. In my case I'm consuming an api, so passing the credentials doesn't involve any action from the final user. For me it's just to params more to send on the POST request.
In both cases, I have to issue a new post every 5 minutes.
Is that the only advantage (not needing to pass credentials again) of using the reresh token or is there any other thing I'm missing?
Background info
OAuth 2.0 Password Grant
The Password grant type is a way to exchange a user's credentials for an access token. Because the client application has to collect the user's password and send it to the authorization server, it is not recommended that this grant be used at all anymore.
OAuth 2.0 Refresh Token
The Refresh Token grant type is used by clients to exchange a refresh token for an access token when the access token has expired.
This allows clients to continue to have a valid access token without further interaction with the user.
Consider this.
Lets say that i add my login and password for my twitter account to your application and you then use that to request access from twitter to may account to post. Three months later i have forgotten i have set your awesome app up to do something on my twitter account and i change my password. Your system will break.
Now lets say i used Oauth2 to grant you access to my Google drive account, your awesome app can now do what ever it needs to do on my drive account. Now three months latter i have again forgotten i gave your awesome app access, I have the memory of a gold fish you see. I change my password. Nothing happens your awesome app still has access.
Now consider this, With oauth2 i can grant you access to only read from my google drive account not update it (scope). That and the system knows its not actually me preforming the actions.
With client login (login and password) most of the time it appears to the system that it is the actually owner of the account making the requests. You can also not limit access with client login for the most part you have full access.
note
yes i am ignoring the part about both tokens returned being the expiration time time. Thats because for all intensive purposes they are the same but that depends greatly on how the auth server you are using is set up. They could be set up to only be valid for an hour or a day. They may give you different access scopes, again this differs greatly from auth server to auth server.

how to use oauth2 token permanently?

I would like to use the oauth2 token permanently in my website.
Using google API, when the user giving permission to manage their google calendar, I am getting the access token from google API. I want to save it in database and use it for the last long.
But it is getting invalid. I tried to refresh the token using the oauth2 refresh token API. But getting the error as follows.
Array ( [error] => invalid_grant [error_description] => Token has been expired or revoked. )
I don't want the user to give the permission each time when he enters the website.
I would also like the admin need to access the user's calendar using this token or any other way if it is.
How can I achieve this?
Instead trying to get the permanent token, I am using the refresh token option.
When I call the google calendar using tag, I have added the parameter like "access_type=offline" as follows
$login_url = 'https://accounts.google.com/o/oauth2/auth?scope=' . urlencode('https://www.googleapis.com/auth/calendar') . '&redirect_uri=' . CLIENT_REDIRECT_URL . '&response_type=code&client_id=' . CLIENT_ID . '&access_type=offline';
After the user allows, I'll get access_token with refresh_token as response. I am saving those in database. Then everytime, when the user's calendar access by the admin, will get the new access_token using the refresh_token that is stored in the database through the refresh_token api call until the user use the unsynchronize option in my site.
Once the user unsynchronized the calendar, I'll update the database.
Problem is resolved.
Bottom line is you can't get a permanent token - but you can mitigate the need for your user to re-supply credentials.
The solution depends what grant type you are using (which depends largely on whether your application runs on a server or or a end-user's machine). You mention a website so hopefully you use a grant type which returns a refresh code too.
If your app runs on a server and you get a token via the authorisation code grant then you should be able to also get a refresh token when you get an access token. You can use that refresh token to request new refresh/access tokens on a back-channel, without need of your user, or their credentials.
Effectively you should then have long-lived access to your user's google resources providing the user doesn't revoke access.
If you use other grant types, like implicit grant, then you can't get a refresh token. You will need to regularly obtain a new access token. If your user remains logged-in to google on the device your app is running on then they should not be required to supply their google credentials when you request a new access token, so you won't be constantly pestering them for credentials.

Refreshed Token could be revoked 50 times per account

I tried to add YouTube Video from the third party and After one day, I got the success in doing so. But While uploading a video the access token is required and in order to get that access token the user must be logged in. And the expiration time for that access token is 3600 seconds( 1 hr).
Now, There are some of my questions regarding this.
Is there anyway, by which I can refresh access token.
If some one has G Suite account, then Is there any special values for expiration time, or it remains the same?
As per the documentation, I can have maximum 50 tokens, So is there any alternative for it, So that I can get valid token after 50 requests.
To answer your question for number 1, you can check the documentation here.
Access tokens periodically expire. You can refresh an access token
without prompting the user for permission (including when the user is
not present) if you requested offline access to the scopes associated
with the token.
If you use a Google API Client Library, the client object refreshes the access token as needed as long as you configure that
object for offline access.
If you are not using a client library, you need to set the access_type HTTP query parameter to offline when redirecting the
user to Google's OAuth 2.0 server. In that case, Google's
authorization server returns a refresh token when you exchange an
authorization code for an access token. Then, if the access token
expires (or at any other time), you can use a refresh token to obtain
a new access token.
Requesting offline access is a requirement for any application that
needs to access a Google API when the user is not present. For
example, an app that performs backup services or executes actions at
predetermined times needs to be able to refresh its access token when
the user is not present. The default style of access is called online.
About the G Suite account, it was stated 24 Hours in the documentation. Note:
In this SO post answer, the function of Access Token and Refresh Token was discussed.
I am not sure if there are ways to alter the limits because of security reasons.
To clearly differentiate these two tokens and avoid getting mixed up,
here are their functions given in The OAuth 2.0 Authorization
Framework:
Access Tokens are issued to third-party clients by an authorization server with the approval of the resource owner. The
client uses the access token to access the protected resources hosted
by the resource server.
Refresh Tokens are credentials used to obtain access tokens. Refresh tokens are issued to the client by the authorization server
and are used to obtain a new access token when the current access
token becomes invalid or expires, or to obtain additional access
tokens with identical or narrower scope.

Oauth SSO for REST applications

I am building a (set of) web application; the backend has REST-like API, the frontend will be some REST JS app, android apps etc; and I'm trying to come up with an SSO functionality.
Looking at Oauth2/OIDC it seems the best way would be to use Implicit flow; however, the access tokens in implicit flow (in oidc) have a set expiration. The refresh token is not part of implicit flow.
How do I ensure that the user will stay logged in? I.e. when the access token expires, the frontend application will try to obtain a new one from an auth server; that is supposed to ask for username/password. Alternatively, it can build a session with the frontend (using cookies), but how is that different from a refresh token?
It seems to me that getting the access token e.g. from the android app means at least opening the web browser; depending on the expiry length, that could be quite often. Is that the correct flow or am I missing something?
You are right, the issuance of a refresh token is not allowed with the Implicit grant type.
However, the refresh token and the access token are not needed to know if the user is logged in or not (the access token only allows you to access on protected resources). You have to use the ID Token which is issued in the authorization response.
You can verify if the user is still logged in by sending an authorization request with the query parameter prompt=none (see section 3.1.2.1. Authentication Request). I recommend you to send the current ID Token using the id_token_hint query parameter as mentioned in the same section:
ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client. If the End-User identified by the ID Token is logged in or is logged in by the request, then the Authorization Server returns a positive response; otherwise, it SHOULD return an error, such as login_required. When possible, an id_token_hint SHOULD be present when prompt=none is used
If you receive an error (login_required or interaction_required) then the user may be logged out.
Another way could be to use the Session Management feature. But as this specification is not yet approved (draft 27), it may be subject to changes and may not be available. However it is a very simple way to know the status of the user.

Getting "invalid_grant" error when exchanging a refresh token for an access token

I've seen this issue in a lot of questions, but so far, none seem to apply to my situation.
The problem we are having is we are getting an "invalid_grant" error when we attempt to get an access token. This only happens to some accounts, but when it does happen, in every case I looked at, the refresh token worked before, and now has stopped working. This is happening far to frequently for it to be customers revoking access (seems to be nearly 20% of the channels we manage in the last couple weeks have been invalidated).
As a note, we have a backend process that uploads the videos to our customer's YouTube channels.
We use OAuth2 to get a refresh token, here are the parameters we send...
scope = "https://www.googleapis.com/auth/youtube",
client_id = "",
response_type = "code",
access_type = "offline",
approval_prompt = "force",
redirect_uri = "http://www.us.com/OAuth/YouTube"
NOTE: for client_id we use the email address that is in the Google API manager (or was, I just looked and it is no longer there). We used to use the client ID from this page, but that caused us problems as well. Did this change? Should we be using the client ID from this page now?
We exchange the code that is returned for a refresh and access token and store the refresh token in our database.
The backend process exchanges the refresh token for an access token and this is where we seem to be getting the "invalid_grant" error.
Guaranteed only a single access token for the channel is in use at any time (25 limit doesn't apply). We don't store the access token, we get a fresh one every time we process a channel.
Any ideas what might be happening? Something to look for? See note above about client ID. This might have something to do with it, but I'm hesitant to try it since using the "Client ID" from the API manager caused problems before.
Guaranteed only a single access token for the channel is in use at any time (25 limit doesn't apply). We don't store the access token, we get a fresh one every time we process a channel.
This statement is incorrect: Access tokens can be used as many times as you need while they are still good (for an hour).
Answer:
"invalid_grant" basically means that your refresh token no longer works. The only solution to the problem is to request access again and get a new one. The question should be why is it expiring in the first place.
Assuming that the user did not revoke access, and that the refresh token has been used to request a new access token within the last six months. This is probably an issue with it being over written.
When a user authenticates your application you are given a refresh token. This refresh token is associated to the client id of your application and the user who has just authenticated. If said user then authenticates your application again you will get another refresh token. Again this refresh token is associated to the user and your projects client id. Both of these refresh tokens will work. Your user can keep doing this up to 25 (Note I think the changed it recently to 50 but I haven't tested it with all APIs yet) once they have hit this magic number the first refresh token will expired and if you try and use it you will get an invalid grant.
The only solution is then to just request authentication again. It is important to always save the most recent refresh token that your user has granted your application. In the event (like me) you have an application that is stored on a number of servers all requiring authentication. Your going to have to tell them not to refresh it to many times or they will have to go back and reauthenticate the first one that they expired.
If this is happening with ALL of your requests. You can also check that you server is sync with (NTP) and that you are sending the payload of your request in the post field. Not attached to the authentication end point like a HTTP GET (been there done that).
Here are the possible reasons why a token stops working and becomes invalid:
The user has revoked access.
The token has not been used for six months.
The user changed passwords and the token contains Gmail scopes.
The user account has exceeded a certain number of token requests.
As you can see, it's not recommended that you request a fresh one every time you process a channel. As also mentioned in Token expiration:
If you need to authorize multiple programs, machines, or devices, one workaround is to limit the number of clients that you authorize per user account to 15 or 20. If you are a Google Apps admin, you can create additional admin users and use them to authorize some of the clients.
With regards to the use of client_ID, it is usually needed to call the sign-in API as mentioned in Creating a Google API Console project and client ID.
And lastly, this Google Groups discussion - OAuth 2.0 400 - error:invalid_grant and ideas? might also help.

Resources