How to refresh Google OAuth 2 Access Token Kotlin - java

I am trying to build an application that interacts with Google's API to pull data from the YouTube Data API. I have been able to get an access token and a refresh token by having the user authenticate manually but I want the program to run without user input. My current approach is to get a new access token each time the application run but this seems to, after some time, revoke the current refresh token for the user which I used to get a new access token. My code is below. Does anyone have any suggestions for what I am doing wrong or a better way to do this? Thanks
val scopes: ArrayList<String> = ArrayList()
scopes.add("https://www.googleapis.com/auth/youtube.readonly")
val tokenResponse: TokenResponse = GoogleRefreshTokenRequest(NetHttpTransport(), GsonFactory(), refreshToken, clientID, clientSecret)
.setScopes(scopes).setGrantType("refresh_token").execute()
tokenResponse.accessToken
val cred: GoogleCredential = GoogleCredential.Builder().setTransport(NetHttpTransport()).setJsonFactory(GsonFactory()).setClientSecrets(clientID, clientSecret).build().setAccessToken(tokenResponse.accessToken).setRefreshToken(tokenResponse.refreshToken)

Related

How to use MS GraphService client to use the access token for authentication

I am trying to build a client system which fetches the inbox of the user emails in outlook. The requirement is that the system should ask the user to login through Outlook once to authenticate and it should be able to fetch the emails and other actions enables via graph API like reply, forward etc there after without any login prompt. For this I tried using the MS Graph SDK and chose the authorization code flow for the same . I followed the following steps
Requested an authorization code by using the link : https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?client_id=clientId&response_type=code&redirect_uri=http%3A%2%2Flocalhost%2Fmyapp%2F&scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read%20api%3A%2F%2F
I have stored this authorization code to the db and used the following snippet for authorization.
final AuthorizationCodeCredential authCodeCredential = new AuthorizationCodeCredentialBuilder()
.clientId("client id")
.clientSecret("client secret")
.authorizationCode("authcode from the db for that user")
.redirectUrl("http://localhost:8080/redirect/url")
.build();
final List<String> scopes = new ArrayList<String>();
scopes.add("Mail.Send");
scopes.add("offline_access");
scopes.add("openid");
final TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(scopes, authCodeCredential);
GraphServiceClient graphClient = GraphServiceClient.builder().authenticationProvider( authProvider ).buildClient();
MessageCollectionPage messages = graphClient.me()
.messages()
.buildRequest()
.get();
This works the first time the fetched authorization code is fetched by user interaction , but when the same code is reused to avoid user interactions it throws the following
AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token.
Does this mean that to make this flow work I have to request my users to login everytime for a single API call via graph client. Is there any way in which we can use the access code got in the AuthorizationCodeCredential and use it with GraphServiceClient to access microsoft Graph APIs using the SDK . Will hugely appreciate any kind of help on this. thank you

OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token

Why I am not able to call graphClient more then once?
Code:
public static void initializeGraphAuth(String authorizationCode) {
List<String> scopes = new ArrayList<>();
scopes.add("https://graph.microsoft.com/mail.read");
// Create the auth provider
final AuthorizationCodeCredential authCodeCredential = new AuthorizationCodeCredentialBuilder().clientId(AzureConstants.CLIENT_ID).clientSecret(AzureConstants.CLIENT_SECRET).authorizationCode(authorizationCode) .redirectUrl(AzureConstants.REDIRECT_URI).build();
authProvider = new TokenCredentialAuthProvider(scopes, authCodeCredential);
// Create default logger to only log errors
DefaultLogger logger = new DefaultLogger();
logger.setLoggingLevel(LoggerLevel.ERROR);
// Build a Graph client
graphClient = GraphServiceClient.builder().authenticationProvider(authProvider).logger(logger).buildClient();
}
public static User getUserDetails() {
return graphClient.me().buildRequest().get();
}
public static List<Group> getUserGroups() {
GroupCollectionPage groups = graphClient.me().transitiveMemberOfAsGroup().buildRequest().get();
return groups.getCurrentPage();
}
In main app I am calling getUserDetails() and getUserGroups() methods to get users details and group details respectively. Able to get User details but not group details below is the error
com.microsoft.aad.msal4j.MsalInteractionRequiredException: AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token.
Trace ID: 48d1fee1-cb8b-48c6-a7ec-91e2b2057500
Correlation ID: c58388ec-417c-4398-82ee-68910568f4df
If i call only one method either getUserDetails or getUserGroups its is working fine, but when i call both methods in code it is giving error
How can i use graphClient object to get user and group details both??
Thanks for your help
When your application uses authorization codes to obtain tokens, this behavior is to be expected.
In this situation, refresh tokens can be used to obtain extra tokens for other resources.
Refresh tokens can be used several times across multiple resources, whereas authorization codes can only be used once.
When Credential uses a refresh token, it also updates the access token when the access token expires.
You can receive a new access token using a refresh token by using the Google OAuth2 client library.
For more information on this, you can refer OAuth 2.0 and the Google OAuth Client Library for Java
REFERENCES:
OAuth2 Authorization code was already redeemed - Microsoft Q&A
How to get an access token using a refresh token in Java? - Stack Overflow

How to get access token using refresh token?

In my app we want to provide login with google. So i went throught the folowing Quickstart and created a sample extending it by setting access_type to offline. So that i can get refresh token as their will be background task that will perform operation on those account after a particular interval of time, example: saving starrred mail in some file on server for each user.
So i need to know:
How to save refresh token ? like save it in DB with userId or somthing or save whole credential object as few post i have seen use credential object
How to use refresh token to get accesstoken ?
When does refresh token expires?
Code reference will be very useful.
Thank you!!
1.You can save the refresh token in DB as like userId, you can get new access token by using the refresh token
2.You need to pass the Client Id, Client Secret and Refresh token to get the new access token
3.Refresh tokens are valid until the user revokes access.

Hardcoding access token does not work in google spreadsheet api?

Using this example ( https://developers.google.com/google-apps/spreadsheets/#creating_a_spreadsheet ), I am able to login and use the Google spreadsheet api using oAuth 1.0 at the moment, because they have a java sample for that.
Here, it gets the access token + secret, and, subsequent calls to the SpreadsheetService work.
But if i want to come back a day later, and use the same access token + secret, that should work as well right?
If i do this, however, it gives me an exception:
com.google.gdata.util.AuthenticationException: Unknown authorization header
What am i missing? Do i have to redirect the user to that URL all the time?
My Java code looks as follows:
SPREADSHEET_FEED_URL = new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();
GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(signer);
oauthParameters.setScope(SCOPES);
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY); // hardcoded variable
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);// hardcoded variable
oauthParameters.setOAuthTokenSecret(OAUTH_ACCESS_SECRET);// hardcoded variable
oauthParameters.setOAuthToken(OAUTH_ACCESS_TOKEN);// hardcoded variable
service.setOAuthCredentials(oauthParameters,signer);
SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
What am i missing?
use the refresh token to get a new access token. The access token does not last long, maybe 1 hour, something like that. The google drive DrEdit tutorial has most of the code for doing the refresh. Was not hard to change the DrEdit code to get a new token. .... (on the other hand, google apps script also has a spreadsheet API)

Authenticate programmatically to Google with OAuth2

How can I authenticate programmatically to Google?
Now that ClientLogin (https://developers.google.com/accounts/docs/AuthForInstalledApps)
is deprecated, how can we perform a programmatic authentication to Google with OAuth2?
With ClientLogin we could perform a post to
https://www.google.com/accounts/ClientLogin
with email and password parameters and obtain the authentication token.
With OAuth2 i can't find a solution!
#
My app is a java background process.
I saw, following this link: developers.google.com/accounts/docs/OAuth2InstalledApp#refresh, how to obtain a new access token using a refreshed token.
The problem is that I can't find a java example about how to instantiate an Analytics object (for example) to perform a query when I have a new valid access token
This is my code that returns a 401 Invalid credentials when invoke the "execute()":
public class Test {
static final String client_id = "MY_CLIENT_ID";
static final String client_secret = "MY_SECRET";
static final String appName = "MY_APP";
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
static String access_token = "xxxx";
static String refreshToken = "yyyyy";
public static void main (String args[]){
try {
GoogleCredential credential =
new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setClientSecrets(client_id, client_secret).build();
credential.setAccessToken(access_token);
credential.setRefreshToken(refreshToken);
//GoogleCredential
Analytics analytics = Analytics.builder(HTTP_TRANSPORT, JSON_FACTORY)
.setApplicationName(appName)
.setHttpRequestInitializer(credential)
.build();
Accounts accounts = analytics.management().accounts().list().execute();
} catch (Exception e) {
e.printStackTrace();
}
}
What is the problem?
Check the OAuth 2 flow for Installed Application:
https://developers.google.com/accounts/docs/OAuth2InstalledApp
It still requires the user to authenticate with a browser the first time, but then you can store the refresh token and use it for subsequent requests.
For alternative solutions, check the Device flow or Service Accounts, they are explained in the same documentation set.
I found the Google Java client to be overly complex and poorly documented. Here's plain and simple Servlet example with Google Oauth2. For a background process you'll need to request access_type=offline. As others have mentioned you need the user to do a one time authorization. After that you can request refresh tokens as google tokens expire in an hour.
Although I appreciate that the OP was originally targeting the OAuth2InstalledApp approach, I would like to point out a working solution using the OAuth2WebServer approach. They don't differ significantly and this worked for me. I have found the google OAuth library to be pretty good as it will handle most of the OAuth dance for you and it makes it easy to refresh the access token. The solution below depends on using a pre-obtained refresh token.
As the accepted answer states, to get OAuth authentication working (even for a Java background process) where the request relies upon access to user data
requires the user to authenticate with a browser the first time, but then you can store the refresh token and use it for subsequent requests.
From previous comments by the OP I see the following
So I followed OAuth2 for Web Server Applications (here offline access is documented) but I have still problems.
1) I perform the first request via browser and I obtain autenticaton code for offline access
2) I perform a java post of the authentication code and obtain acces token and refresh token
The approach I used is more like
1) I perform the first request via a browser and obtain the refresh token for offline access
2) In java I provide the refresh token to the library and the library will obtain the access token etc
specifically, using the google-api-java-client library the code is quite straightforward and note that I haven't set an access token as the OP did, as I am calling credential.refreshToken(); elsewhere. (I check if I have a valid access token already and if not call refresh prior to the API call)
private Credential generateCredentialWithUserApprovedToken() throws IOException,
GeneralSecurityException {
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
InputStreamReader inputStreamReader =
new InputStreamReader(jsonFileResourceForClient.getInputStream());
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(jsonFactory, inputStreamReader);
return new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory)
.setClientSecrets(clientSecrets).build().setRefreshToken(REFRESH_TOKEN);
}
Note this covers step 2 of my approach, and the REFRESH_TOKEN mentioned in step 1 can be obtained as explained below.
First there is a prior set up of a web app creating an OAuth 2.0 client ID on the Google console for Credentials where you end up with a downloaded json file which will be read into the GoogleClientSecrets object.
i.e.
Make sure you add the Google playground callback uri into Authorized redirect URIs
Then you have your client id and the client secret ready for the playground and you can also download the json which you can pull into your Java code.
The REFRESH_TOKEN is obtained by sending a request to the google oauth playground with the following configuration. Note that prior to Step 1 and selecting your scope you should go to settings to check that you are providing you own credentials and add your client id and secret just below that
Note that the Access type is Offline, which corresponds to this.
There is also a nice explanation on grabbing the refresh token here https://www.youtube.com/watch?v=hfWe1gPCnzc
That is enough to get going and is a one time set up!
Regarding refresh tokens you should be aware of their lifecycle as discussed in the docs here
In the oauthplayground you will see this
but on point 4 of the docs here it says this
Hmmm.
Also for reference see How do I authorise an app (web or installed) without user intervention? (canonical ?)
For applications that authenticate on behalf of themselves (i.e., to another application, traditionally by signing into a role account using a shared password), the OAuth2 alternative to ClientLogin offered by Google is Service Accounts:
https://developers.google.com/accounts/docs/OAuth2ServiceAccount

Categories

Resources