Fetching access token from refresh token using Java - google-drive-api

I got the refresh token and access token from the authorization code by using the sample program given here https://developers.google.com/drive/credentials#retrieve_oauth_20_credentials
But there is no sample program to get the access token from refresh token i.e., when we dont have authorization code. Any pointers? Is there any way to Instantiate a drive service object using only refresh token and access token?

The DrEdit Java sample has an example on how to retrieve stored Credentials from the Google App Engine Datastore.
If using another type of credentials store, you can use the following code to instantiate new OAuth 2.0 Credentials using stored tokens:
GoogleCredential credentials = new GoogleCredential.Builder()
.setClientSecrets(CLIENT_ID, CLIENT_SECRET)
.setJsonFactory(jsonFactory).setTransport(transport).build()
.setRefreshToken("<REFRESH_TOKEN>").setAccessToken("<ACCESS_TOKEN>");
EDIT: Corrected a typo in the code.

Try this code. This is a mix solution from Alain + https://cloud.google.com/bigquery/authorization. this worked for me.
GoogleCredential credential = createCredentialWithRefreshToken(
HTTP_TRANSPORT, JSON_FACTORY, new TokenResponse().setRefreshToken(refreshToken));
credential.refreshToken();
String newAccessToken = credential.getAccessToken();
public static GoogleCredential createCredentialWithRefreshToken(HttpTransport transport,
JsonFactory jsonFactory, TokenResponse tokenResponse) {
return new GoogleCredential.Builder().setTransport(transport)
.setJsonFactory(jsonFactory)
.setClientSecrets(CLIENT_ID, CLIENT_SECRET)
.build()
.setFromTokenResponse(tokenResponse);
}

Just to explain my experience.
Had the same problem (access token to null) the problem was that I had been tested without sending setAccessType ("offline") and from the account access was allowed access.
Then I put on my code setAccessType ("offline") code but still refresh token to null.
My solution was to revoke permission from the account I want to access (https://accounts.google.com/b/0/IssuedAuthSubTokens?hl=en). In the next test I grant it.

Follow this example:
private static void getAccessTokenFromRefreshToken() throws IOException {
GoogleCredential credentials = new GoogleCredential.Builder()
.setClientSecrets(CLIENT_ID, CLIENT_SECRET)
.setJsonFactory(JSON_FACTORY).setTransport(httpTransport).build()
.setRefreshToken(REFRESH_TOKEN);
String accessToken = credentials.getAccessToken();
System.out.println("Access token before: " + accessToken);
credentials.refreshToken();
accessToken = credentials.getAccessToken();
System.out.println("Access token after: " + accessToken);
}
Output:
Access token before: null
Access token after: ya29.u4HC22-Avc0aaDC0g0zj1jhz2yjsJrm8qm0hU2eVeBrf6DKj3CcHDQ42KARH4y_d364-b

Related

Passing wallet argument in ethers Contract function

I am trying to create an instance of a contract on ethers.
const example = new ethers.Contract(CONTRACT_ADDRESS, contractABI, signer);
I am using a wallet directly instead of getting a signer through metamask ( I am aware of the risks)
Here is my "signer":
const signer = new ethers.Wallet("PRIVATE_KEY");
When i try to execute the code I get:
ethers-5.5.4.esm.min.js:1 Uncaught (in promise) Error: invalid address or ENS name (argument="name", value={"_isSigner":true,"address":"0x92388d12744B418eFac8370B266D31fd9C.....","provider":null}, code=INVALID_ARGUMENT, version=contracts/5.5.0)
Do I have a syntax error? or is this not the correct way to pass a wallet as a signer
I think you might miss your provider there, you'll need to add provider object like this
const url = "RPC Node URL"
const provider = new ethers.providers.JsonRpcProvider(url);
const signer = new ethers.Wallet("PRIVATE_KEY", provider);
Use the right RPC Node network to interact with the right blockchain. You can either use public or private RPC, but for production it is recommended to use private services.
You did not connect to the signer. From docs
// this creates a new contract address
new ethers.Contract( address , abi , signerOrProvider )
// Returns a new instance of the Contract, but connected to providerOrSigner.
contract.connect( providerOrSigner ) ⇒ Contractsource
In your code:
example.connect(signer)

Google Drive API - Granting Access to Files in a Folder to Service Account

I am using a service account to connect to the Google Drive of a G Suite. I understand that with the available access (scope: https://www.googleapis.com/auth/drive.file) I can only see the files that the service account has created and using the /auth/drive scope (which gives full visibility) requires special Google approval.
I need to be able to expand the service account's visibility to include files in at least 1 folder the user has not created. I can't get this through normal folder sharing as best I can tell.
Does anyone know how to do this?
Edit to include code (written in Apex, which is similar to Java). Still pretty rough, I haven't cleaned it up yet but:
private static String buildAuthBody(Google_Drive_Integration__mdt mdt) {
//Builds and encodes the JWT header
String bodyHeader = '{"alg":"RS256","typ":"JWT"}';
String encodedHeader = encode(bodyHeader);
//Builds and encodes the JWT Claim Set. See googleAuth body
googleAuth ga = new googleAuth(mdt);
String claimSetString = JSON.serialize(ga);
String encodedClaimSet = encode(claimSetString);
//Builds out necessary pieces for Crypt.sign(algorithmName, input, privateKey). Input = body
String signatureBody = encodedHeader + '.' + encodedClaimSet;
signatureBody = signatureBody.replaceAll('=','');
String encodedSignatureBody = EncodingUtil.urlEncode(signatureBody,'UTF-8');
Blob signatureBodyBlob = Blob.valueOf(encodedSignatureBody);
Blob key = EncodingUtil.base64Decode(mdt.Service_Account_Private_Key__c); //Must be decoded to pass into method w/o error
//Builds the signature
Blob signatureBlob = Crypto.sign('RSA-SHA256', signatureBodyBlob , key);
String encodedSignature = encodeBlob(signatureBlob);
//Sets grant type
String grantType = EncodingUtil.urlEncode('urn:ietf:params:oauth:grant-type:jwt-bearer', 'UTF-8');
//Sets body and debugs to rebuild body
System.debug('grant type: grant_type=' + grantType);
System.debug('&assertion=');
System.debug('encoded header: '+encodedHeader);
System.debug('encoded claim set: '+encodedClaimSet);
System.debug('encoded signature: '+encodedSignature);
//Build and return the body
String body = 'grant_type=' + grantType;
body += '&assertion=';
body += signatureBody;
body += '.' + encodedSignature;
return body;
}
class googleAuth {
public String iss; //'test-google-drive#sapient-flare-252622.iam.gserviceaccount.com';
public String scope = 'https://www.googleapis.com/auth/drive';
public String aud = 'https://www.googleapis.com/oauth2/v4/token';
public Long exp;
public Long iat;
googleAuth(Google_Drive_Integration__mdt mdt) {
DateTime dt = DateTime.now();
iat = dt.getTime() / 1000;
exp = iat + 3600;
iss = mdt.Service_Account_User_Email__c;
}
}
private static String encode(String str) {
Blob b = Blob.valueOf(str);
String ret = EncodingUtil.base64Encode(b);
ret = EncodingUtil.urlEncode(ret, 'UTF-8');
return ret;
}
private static String encodeBlob(Blob b) {
String ret = EncodingUtil.base64Encode(b);
ret = EncodingUtil.urlEncode(ret, 'UTF-8');
return ret;
}
I think you are misunderstanding things about service accounts and about scopes.
Scopes define the amount of access a user has granted to your application. In this case the user is the service account. Using scope: https://www.googleapis.com/auth/drive.file with a service account doesn't make much sense really as you the developer own the service account and there by own its drive account. so really just give it full access using scope: https://www.googleapis.com/auth/drive there is no real reason for you to limit it. If you had a normal application using Oauth2 you would only request the access you need to the users drive account.
I need to be able to expand the service account's visibility to include files in at least 1 folder the user has not created. I can't get this through normal folder sharing as best I can tell.
I am not sure i understand this. Assuming that when you say "user" you mean the service account. I am going to assume this folder is part of your gsuite system. In which case you simply need to have your Gsuite admin set up domain wide delegation to the service account and it will have access. This is a way of granting the service account permissions to access data on your gsuite account kind of like adding a new user to the system.

authentication AWS method Rest DSL

I have a question regarding Restful services
I need to upload CSV files to an AWS server. I am registered with account.
First I need to obtain an access token and use that to upload the files. I have not coded anything yet, trying to understand the best approach, I so hope to use Camel-Rest-DSL. It is required to communicate with JSON. But, the authentication part has me stuck, I’m pretty sure it uses OAuth2 auth, RestFul web service and JSON, this should just be a client, I was looking at WSS4J for JAX-RS OAuth2 but I don’t know.
I’ve done it with postman, this is the scenario. The username and password are fictional
*Get Access Token
uses POST verb
requires Token Request URL
uses Basic Auth requires Username = Client ID of tenant ( needs to be encoded base64 )
HEADER parm Content-Type = x-www-form-urlencoded
Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + encoded Client ID
Access Token body - grant_type, username, password
Body = username = 5c0642fe-a495-44db-93f7-67034556fa2c061_ingestor
password = 154f0252d166f27b5e21ef171b03212a79f41a0daf3
grant_type = password
#returns the access_token as JSON
POST or upload files
uses POST verb
requires Ingestion URL UploadURL
UploadURL=https://apm-ts-query-svc-prd.app-api.aws-usw02-pr.something.io/v2/time_series/
UploadFolder=upload
headers =
key Authentication "Bearer + access Token" (from request access token above)
key Tenant = TenantUUID
key X-GE-CsvFormat = ODB
# Body
form-data
key file=file
# POST DATA
headers content-type application/json
authorization: "" + token
tenant: "" + tenant
My environment
Jboss Fuse 6.3-310
Karaf version 2.4.0.redhat-630310
JVM
Java Virtual Machine Java HotSpot(TM) 64-Bit Server VM version 25.162-
b12
Version 1.8.0_162
Vendor Oracle Corporation
Operating system
Name Linux version 2.6.32-696.20.1.el6.x86_64
I can't use OAuth2/SAML assertions so I will simply request a token and cache it and use it later. this is my test code
#Override
public void configure() throws Exception {
//Configure the Rest Web Service Component transport to use a REST implementation
restConfiguration() //Configures the REST DSL to use a specific REST implementation
.component("jetty") //Specifies the Camel component to use as the REST transport
.host("localhost") //The hostname to use for exposing the REST service
.port("8282") //The port number to use for exposing the REST service JMX tooling
.scheme("https") //The protocol to use for exposing the REST service
.contextPath("/oauth/token") //Sets a leading context path for the REST services
.bindingMode(RestBindingMode.json) //Enables binding mode for JSON
.jsonDataFormat("json-jackson") //Specifies the component that Camel uses to implement the JSON data format
.dataFormatProperty("prettyPrint", "true"); //set arbitrary properties on the underlying data format component
//Configure the Rest Endpoint
rest("/oauth") //Defines a service using the REST DSL. Each of the verb clauses are terminated by a to() keyword,
//which forwards the incoming message to an endpoint
.post("/token")
.produces("application/json")
.consumes("application/json")
.type(TokenEntities.class)
.route()
.routeId("Get Auth Token Route")
.autoStartup(true)
.id("Get Auth Token Service")
.description("Get Authorization Token")
.process(new UAARequestTokenProcessor())
.to("https://d1e53858-2903-4c21-86c0-95edc7a5cef2.pager-uaa.run.aws-usw02-pr.ice.pager.io/oauth/token")
.to("log:logger?showBody=true")
.to("direct:accessToken")
.endRest();
//Define the Route - from() Defines a regular Camel route.
from("direct:accessToken").to("log:logger?showBody=true"); }
public class UAARequestTokenProcessor implements Processor {
private static final Logger LOG = LoggerFactory.getLogger(UAARequestTokenProcessor.class);
private String clientId = "myClientID";
private String userName = "myUserName";
private String password = "myPassword";
#Override
public void process(Exchange exchange) throws Exception {
LOG.info("Processing UAA token request for " + clientId + " and " + userName);
Message msg = exchange.getOut(); //create outbound message exchange
StringBuilder authHeader = new StringBuilder("Basic ");
authHeader.append(Base64.getEncoder().encodeToString((clientId + ":").getBytes("UTF_8")));
String body = String.format("grant_type=password&username=%s&password=%s",
URLEncoder.encode(userName, "UTF-8"), //Translates a string into x-www-form-urlencoded format
URLEncoder.encode(password, "UTF-8"));
msg.setHeader(Exchange.CONTENT_TYPE, "MediaType.APPLICATION_FORM_URLENCODE");
msg.setHeader("Authorization", authHeader.toString());
msg.setBody(body);
}
}

Get group members with an admin account / deprecated GroupsManager class

The class GroupsManager will be turned off by November 20, 2014 https://developers.google.com/apps-script/reference/domain/groups-manager
I am using this class to manage some groups with my admin account as it's allowing access to group members WITHOUT BEING MEMBER OF.
Sample code here:
var group = GroupsManager.getGroup("some-group-name#mydomain.com");
var members = group.getAllMembers();
Using the Groups Service https://developers.google.com/apps-script/reference/groups/ is not an option, as stated in the documentation: "This service allows scripts to access Google Groups. It can be used to query information such as a group's email address, or the list of groups in WHICH THE USER IS MEMBER OF."
Using the AdminSDK as proposed in the GroupsManager "To manage your domain, use the Admin SDK Directory and Admin SDK Reports advanced services instead." is neither an option as there is no method to retrieve the members of a group...
So, anyone has a clue?
Thank you,
Franck
You can use the Admin SDK Directory service to do this. The method AdminDirectory.Members.list lists all the members of a group.
Directory service = null;
try
{
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Arrays.asList(DirectoryScopes.ADMIN_DIRECTORY_USER))
.setServiceAccountUser(userEmail)
.setServiceAccountPrivateKeyFromP12File(
new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
.build();
service = new Directory.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential).setApplicationName("ShareFileUSer").build();
}catch(Exception e)
{
System.out.println("Exception is : "+e);
}
Directory.Users.List userList = service.users().list().setCustomer("my_customer").setMaxResults(100);

JavaMail SMTPSendFailedException

I am writing a bulk email program using the JavaMail api. I have a Microsoft Exhange server which I am trying to send the emails in to. When I run my program I get the following error:
**com.sun.mail.smtp.SMTPTransport.issueSendCommand(SMTPTransport.java:2057)
at com.sun.mail.smtp.SMTPTransport.finishData(SMTPTransport.java:1862)
at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1100)
at javax.mail.Transport.send0(Transport.java:195)
at javax.mail.Transport.send(Transport.java:124)
at SendEmail.postMail(SendEmail.java:100)
at EmailGenerator.main(EmailGenerator.java:52)**
The part of my code trying to send the message is as follows:
Properties props = new Properties();
props.put("mail.smtp.host", email_server);
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", true);
class EmailAuthenticator extends Authenticator {
String user;
String pw;
EmailAuthenticator (String FROM, String PASSWORD)
{
super();
this.user = FROM;
this.pw = PASSWORD;
}
public PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(user, pw);
}
}
Session session = Session.getInstance(props, new EmailAuthenticator(USER, PASSWORD));
session.setDebug(debug);
System.out.println("Session created");
.. CREATED MESSAGE HERE...
Transport transport = session.getTransport("smtp");
transport.connect(exchange_server,user,password);
transport.send(msg);
transport.close();
I wonder am I missing some configuration on the Exchange server side, or is an issue with my code?
OK I figured out where I was going wrong here and am posting up the answer incase anybody else can get some value out of it. I had the following line of code:
props.put("mail.smtp.auth", true);
This was telling my application that it needed to authenticate to the SMTP server, when in fact it didnt. This was causing my application from logging into the SMTP server and sending the email and thus producing the error message. Setting this property to false or not having this line of code fixed the issue for me. This line of code is only necessary for SMTP servers that require you to login, which my Exchange server didnt.