I have some code in my grails 2.3.4's controller's action that uses google java client libraries to access the OAuth2 api. But when I create an instance of GoogleAuthorizationCodeFlow I get redirect_uri_mismatch error. The url google gives me is this http://localhost:60720/Callback, while I have defined the callback url in the google api console as this http://localhost:8080/<myAppName>/<controllerName>/<actionName>. When I copy paste my redirected url manually in the address bar replacing the one google gave me, my application works well.
I've registered the application as web application not installed application in api console. What can I do? Please help. If I couldn't solve this problem then I'll revert to the REST Api.
ResourceLocator grailsResourceLocator
JsonFactory jsonFactory = JacksonFactory.defaultInstance
File clientSecretsFile = grailsResourceLocator.findResourceForURI("/configs/clientSecrets.json").file
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(jsonFactory, new InputStreamReader(new FileInputStream(clientSecretsFile)))
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport()
FileDataStoreFactory dataStoreFactory = new FileDataStoreFactory(new File(System.getProperty("user.home"), ".store/oauth2_sample"))
List<String> SCOPES = ["https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email"]
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, clientSecrets, SCOPES).setDataStoreFactory(dataStoreFactory).build()
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user")
Thanks.
Okey I've found the solution.
When you create new LocalServerReceiver() use instead new LocalServerReceiver.Builder().setPort(9089).build()
I've chosen some empty port in this case 9089. Actually LocalServerReceiver is a http server that listens to google and google sends code param to this server. Now all you have to do is create a new entry in your Redirected URIs which is in this case as follows: http://localhost:9089/Callback. Note the port number is same as I've used in the code while building the server using LocalServerReceiver.Builder() class.
And voila!!! you got the access_token and refresh_token from the google.
Happy coding... Happy Java clients...
Related
I am working on a Spring-MVC application running on tomcat in which I would like to use Google Business Profile API functionality. I tried with an OAuth 2.0 Client ID on my local machine and I'm getting a redirect_uri_mismatch.
I don't understand one thing, I am setting the redirect URL in google console and in secret JSON, why on earth is it getting the redirect_url with random port numbers. If I change the port number in the browser URL, it works fine. I have set the port number
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver.Builder().setPort(8083).build()).authorize("user");
But get an error Address already binded.
I have 2 to 3 days but no luck till yet.
// Creates an InputStream to hold the client ID and secret.
InputStream secrets =GoogleBusinessMsgsRestService.class.getClassLoader().getResourceAsStream("client_secret.json");
// Uses the InputStream to create an instance of GoogleClientSecrets.
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
new InputStreamReader(secrets));
// Sets up the authorization code flow.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, JSON_FACTORY, clientSecrets,
Collections.singleton("https://www.googleapis.com/auth/business.manage"))
.setDataStoreFactory(dataStoreFactory).build();
// Returns the credential.
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver.Builder().build()).authorize("user");
Scenario: I am working on a Web Application on Google AppEngine where users have to access files from their own Google Drive. I have looked for online help and this is what I have figured out.
I have used this link for help that worked fine while testing with local machine
https://github.com/gsuitedevs/java-samples/blob/master/drive/quickstart/src/main/java/DriveQuickstart.java
Step 1 (Seemed simple): Enable Google Drive API and Setup necessary credential, i.e. OAuth 2.0 Client ID for Web Application. (This is done while enabling IAP on Google AppEngine, so I did not do it again. Whenever anyone opens the web application, he/she is asked to authenticate via iap.googleapis.com and details saved. This works fine). Also, I have added Google Drive Scopes to the OAuth consent screen (../auth/drive.appdata & ../auth/drive.file) that don't need verification by Google.
Step 2: Downloaded the credentials.json from OAuth Client ID and stored inside "resources" folder created in the root of application package (inside main folder, next to java and webapp folders)
Step 3: I have created a testing class (GoogleDriveServiceTest.class) that includes following code:
String USER_ID1 = UserServiceFactory.getUserService().getCurrentUser().getUserId();
List<String> SCOPES = Collections.singletonList(DriveScopes.DRIVE_METADATA_READONLY);
NetHttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
InputStream inputStream =
GoogleDriveServiceTest.class.getResourceAsStream("/credentials.json");
if (inputStream == null)
throw new FileNotFoundException("Required credentials file not found");
GoogleClientSecrets googleClientSecrets =
GoogleClientSecrets.load(jsonFactory, new InputStreamReader(inputStream));
AppEngineDataStoreFactory appEngineDataStoreFactory =
AppEngineDataStoreFactory.getDefaultInstance();
//IS SOMETHING MISSING HERE???
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow
.Builder(httpTransport, jsonFactory, googleClientSecrets, SCOPES)
.setDataStoreFactory(appEngineDataStoreFactory)
.setAccessType("offline")
.build();
Now I am trying to create the credential to be used for accessing Google Drive with this line:
Credential credential = flow.loadCredential(USER_ID1);
that is returning null.
In my opinion I am missing to assign the credentials to AppEngineDataStoreFactory based on what I have seen in the example from the github link above. However, I am not sure if this is the issue, and if it is, how do I resolve it.
Is there a straight forward way to assign credentials using logged in userID obtained from
UserServiceFactory.getUserService().getCurrentUser().getUserId() ? Or should I be obtaining accetoken and create the credential? if so, how?
(I don't want to use javascript as the same does not seem suitable for web application)
Any help would be great!!!!
PS: I also wanted to add a point that user needs to access only files added by the same web application either via web or from android
Update #1 responding to #Aerials:
Here is the code I was trying with to get the TokenResponse:
VerificationCodeReceiver receiver = new GooglePromptReceiver();
(I know above one is not the right option, but I am not able to find any other)
AuthorizationCodeRequestUrl authorizationUrl =
flow.newAuthorizationUrl().setRedirectUri(receiver.getRedirectUri());
String code = receiver.waitForCode();
(Above line returns: java.util.NoSuchElementException: No line found)
TokenResponse tokenResponse =
flow.newTokenRequest(code).setRedirectUri(redirectUri).execute();
Update #2 Code that worked in getting the TokenResponse and rest of the tasks of creating a Credential and connect to Google Drive successfully:
GenericUrl genericUrl = new GenericUrl(request.getRequestURL().toString());
genericUrl.setRawPath("/googleDriveTest");
String redirectUri = genericUrl.build();
(redirectUri should match with authorised redirect URI inside OAuth ClientID under GCP API Credentials. If you added it now, you need to redownload the credentials.json file)
String redirectUrl = authorizationCodeFlow
.newAuthorizationUrl()
.setRedirectUri(redirectUri)
.build();
String authorizationCode = request.getParameter("code");
if (authorizationCode == null || authorizationCode.isEmpty())
response.sendRedirect(redirectUrl);
TokenResponse tokenResponse = authorizationCodeFlow
.newTokenRequest(authorizationCode)
.setRedirectUri(redirectUri).execute();
authorizationCodeFlow.createAndStoreCredential(tokenResponse, USER_ID1);
Credential credential = authorizationCodeFlow.loadCredential(USER_ID1);
Drive service = new Drive.Builder(httpTransport, jsonFactory, credential)
.setApplicationName("myapplicationname")
.build();
You need to first create and store the credentials in the flow's credential store:
createAndStoreCredential(TokenResponse response, String userId)
to be able to load them with loadCredential(String userId)
The loadCredential() method will return credential found in the credential store of the given user ID or null for none found.
I work on student project where I have to use GMAIL API (Java), to connect to mail server, get new messages and download attachments if there is any. I already done this with JavaMail API, but mail server that should use app doesn't accept IMAP and POP3 protocols, it has own web service.
I have two problems.
I have troubles even with starting project in eclipse.
First, I have no idea how to set up connection with mail server (for example gmail account) and how to provide username and password. I saw that gmail api for authorization uses Oauth2.
Second one is maybe easier to solve. When I have established connection to mail server, how to fetch unseen mails and download attachments?
I guess that second part I could do on my own, but without
connection its even useless to try.
I was reading official documentation for few days and I am quite confused.
(Maybe if you have some code sample or similar example it would be nice)
EDIT 1:
Done that, now I have this error
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
setAccessType is not found in com.google.api.client.auth.oauth2.AuthorizationCodeFlow.Builder
All Imports from Quickstart are there.
EDIT 2:
I am quite confused now
Exception in thread "main" java.io.FileNotFoundException: Resource not found: /resources/credentials.json
Line:
private static final String CREDENTIALS_FILE_PATH = "/resources/credentials.json";
File&Folder organization
When I check System.out.print(System.getProperty("user.dir"));
I get F:\NBS\eclipse\FetchMail
EDIT 3:
Deleted all files and started over again but eclipse still looks blind
File not found
EDIT 4:
Tested method .getResourceAsStream(path) inside Main method and it finds credential.json inside \eclipse\FetchMail or any other file i want
Then moved file to \eclipse\FetchMail\resources and call .getResourceAsStream("/resources/credentials.json") also finds file.
But when try this in getCredentials method from Quickstart, there is FileNotFound exception.
private static final String CREDENTIALS_FILE_PATH = "/resources/credentials.json";
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
// Load client secrets.
InputStream in = Main.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
if (in == null) {
throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
}
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
Google uses Oauth2 to authorize and authenticate accounts in order to use the Google APIs. To do so, follow the next steps:
In your Google Developer Console, click on Credentials.
Click on CREATE CREDENTIALS and Select Oauth Client Id
In this case, choose Desktop App
Set any Name and click on Create
You will get a screen with the Client ID and the Client Secret. These keys will authorize your account to use the APIs. They are stored in the credentials file. You don't need to do anything with them, so close the window.
Download the credential's JSON file.
In the Quickstart code, you will notice this line:
private static final String CREDENTIALS_FILE_PATH = "/credentials.json";
You have to set the path of your credentials file. Change the name of the file to yours.
When running the code for the first time, you will get a link to accept the permissions specified in the Scopes. This will generate an Access token which allows the application to access the API without asking for human interaction until it expires.
Finally solved, changed .getResourceAsStream with FileInputStream(path) constructor and it works.
I've been trying to set up an app with it talking to one of the Google APIs. When I deploy it to my linux server (shell only), it does ask me to go and "Please open the following address in your browser" with a redirect URI value of localhost:some_port .
I cannot access it from my linux machine and cannot open it on some other UI-based browser, since the redirect_uri is ALWAYS localhost based.
I've added my server public domain to the redirect URI list, but still it is always generated with localhost address.
How can I get the token from the consent screen, since it always redirects me to localhost on my linux server?
Code
Code for creating connection (all fine on localhost, so no error here):
InputStream in = SearcherApplication.class.getResourceAsStream("/client_secret.json");
GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(jsonFactory, new InputStreamReader(in));
GoogleAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow.Builder(
httpTransport, jsonFactory, clientSecrets, Collections.singleton(YOUTUBE_READONLY))
.setDataStoreFactory(dataStoreFactory)
.setAccessType("offline")
.build();
return new AuthorizationCodeInstalledApp(
flow, new LocalServerReceiver()).authorize("user");
Found the place for that option. Instead of the new LocalServerReceiver() set the following code:
new LocalServerReceiver.Builder()
.setHost("my.awesome.server")
.setPort(8080)
.setCallbackPath("/Callback")
.build()
I am trying to make use of Google Contacts API with OAuth 2 authentication. But some of the old sample codes are now not working for this API.I found a sample code which is makes request for Access Token and in response Google servers would give me the Access Token for ~1 hour limit and a Refresh Token with.
BUT this code has some issue
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
String APPLICATION_NAME = "PROJECT_NAME";
String SERVICE_ACCOUNT_EMAIL = "NUMERCALS-ALPHANUMERICALS#developer.gserviceaccount.com";
java.io.File p12File = new java.io.File("PROJECT_NAME-NUMERICALS.p12");
GoogleCredential credential =
new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(
Collections.singleton("https://www.google.com/m8/feeds"))
.setServiceAccountPrivateKeyFromP12File(p12File)
.setServiceAccountUser("user#example.com")
.build();
if (!credential.refreshToken()) {
throw new RuntimeException("Failed OAuth to refresh the token");
}
ContactsService service = new ContactsService(APPLICATION_NAME);
service.setOAuth2Credentials(credential);
Query gQuery = new Query(new java.net.URL("https://www.google.com/m8/feeds/groups/user#example.com/full"));
gQuery.setMaxResults(32767);
ContactGroupFeed groupFeed = service.query(gQuery, ContactGroupFeed.class);
for (ContactGroupEntry group : groupFeed.getEntries()) {
And I am getting some issues with it
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
"error" : "invalid_grant"
}
at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:269)
at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
at javaCode.FinalCode.main(FinalCode.java:68)
I am new to use OAuth2 for Google APIs so guide me if I am mistaken somewhere or provide a working code snippet.It would be great help.Thanks in advance.
In you example you are trying to use Service account to impersonate the user.
While this is possible, it can only be achieved under Google for work or Google for education accounts.
It would not work for normal gmail accounts.
If this is the case for your use case, then remember that the domain administrator should grant access to your application and also should grant Domain wide delegation of authority.
But if you are just trying to make some tests with the API, then it would be better if you use normal OAuth.
Check this tutorial, I know is for Drive, but there you can see the basic steps to get a authorization token. After you get it, the call to the different Google Apis is very similar.
I would recommend to check the OAuth Playground. There you can check the complete OAuth process and you can play with the API.