Reputation: 43
i'm trying to update google spreadsheet from android application.
I found the flow for such operation should be:
I used Oauth to authorize user, and i received idToken related to his session. However when i create HTTP request with this token attached, i get response 401 unauthorized
E/Volley: [676] NetworkUtility.shouldRetryException: Unexpected response code 401 for <HERE GOES URL OF MY SCRIPT>
2021-11-13 20:47:01.498 20626-20626/com.example.e2 W/System.err: com.android.volley.AuthFailureError
2021-11-13 20:47:01.498 20626-20626/com.example.e2 W/System.err: at com.android.volley.toolbox.NetworkUtility.shouldRetryException(NetworkUtility.java:189)
2021-11-13 20:47:01.498 20626-20626/com.example.e2 W/System.err: at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:145)
2021-11-13 20:47:01.499 20626-20626/com.example.e2 W/System.err: at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:132)
2021-11-13 20:47:01.499 20626-20626/com.example.e2 W/System.err: at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:111)
2021-11-13 20:47:01.499 20626-20626/com.example.e2 W/System.err: at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:90)
What is the correct way i should attach this token, to let google script "know" that this request is authenticated ?
Code of my script:
var ssa = SpreadsheetApp.openByUrl('<URL OF MY SPREADSHEET>');
var sheet = ssa.getSheetByName('<NAME OF TAB>');
function doPost(e){
var action = e.parameter.action;
if(action=='ADD'){
return addItem(e);
}
return 'Unsuported operation';
}
function addItem(e){
var date = new Date();
var id = "Item"+sheet.getLastRow();
var data = e.parameter.data;
sheet.appendRow([date,id,data]);
return ContentService.createTextOutput("Inserting Successfull").setMimeType(ContentService.MimeType.TEXT);
}
Code of my android app:
public class MainActivity extends AppCompatActivity {
private static final int RC_SIGN_IN = 11;
private static String oAuthAccessToken;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if (account != null) {
oAuthAccessToken = account.getIdToken();
}
Button btn1 = (Button) this.findViewById(R.id.btn_login);
btn1.setOnClickListener((v) -> {
loginViaGoogle();
});
Button btn2 = (Button) this.findViewById(R.id.btn_send);
btn2.setOnClickListener((v) -> {
volleySendDataToGSheet();
});
Button btn3 = (Button) this.findViewById(R.id.btn_logout);
btn3.setOnClickListener((v) -> {
logout();
});
}
@NonNull
private GoogleSignInOptions getGoogleSignOptions() {
return new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.server_client_id))
.requestScopes(
new Scope("https://www.googleapis.com/auth/spreadsheets"),
new Scope("https://www.googleapis.com/auth/drive.scripts"),
new Scope("https://www.googleapis.com/auth/script.projects"),
new Scope("https://www.googleapis.com/auth/script.processes"),
new Scope("https://www.googleapis.com/auth/script.projects.readonly"))
.build();
}
private void logout() {
GoogleSignInOptions gso = getGoogleSignOptions();
GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
mGoogleSignInClient.signOut();
}
private void loginViaGoogle() {
GoogleSignInOptions gso = getGoogleSignOptions();
GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, RC_SIGN_IN);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
handleSignInResult(task);
}
}
private void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
oAuthAccessToken = account.getIdToken();
Log.w(TAG, "Login success, user: " + account.getDisplayName() + ", token length:" + oAuthAccessToken.length());
} catch (ApiException e) {
Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
}
}
private void volleySendDataToGSheet() {
String googleScriptAppUrl = "<URL OF MY SCRIPT>";
StringRequest request = new StringRequest(Request.Method.POST, googleScriptAppUrl,
successResponse -> {
Toast.makeText(this, "success: " + successResponse, Toast.LENGTH_LONG).show();
},
volleyError -> {
String message = "error: " + volleyError.getMessage() + " " + volleyError.networkResponse.statusCode;
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
volleyError.printStackTrace();
}
) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("action", "ADD");
params.put("data", "Data value");
Log.i(TAG, "sending params: " + params.toString());
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json");
headers.put("idToken", oAuthAccessToken);
headers.put("id_token", oAuthAccessToken);
headers.put("accessToken", oAuthAccessToken);
headers.put("access_token", oAuthAccessToken);
headers.put("authorization", oAuthAccessToken);
headers.put("Authorization", oAuthAccessToken);
Log.i(TAG, "sending headers:" + headers.toString());
return headers;
}
};
int socketTimeout = 50000;
int retries = 1;
request.setRetryPolicy(new DefaultRetryPolicy(socketTimeout, retries, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(request);
}
}
Google App Script itself works fine, when i test it using chrome Postman extension, it correctly adds data into spreadsheet. So i hope its only a matter of fixing authorization headers in android request.
Thanks in advance!
Upvotes: 0
Views: 231
Reputation: 43
I found a workaround for this authorization problem. If i deploy my Google Apps Script, allowing it to be executed by anyone, it doesnt reject my requests with 401.
Actually, for this project it isn't strictly needed to use my API by authorized user, so i accepted the way it works now.
Otherwise Bearer authorization may be needed, as @TheMaster mentioned
Upvotes: 1