Sending Google Form Responses from Unity 401 Unauthorized - unity3d

Is it still possible to programmatically send "responces" to Google Forms. Preferably without users logging in. I think they changed the protocol.
In a unity application, I need to setup a survey, and the obvious choice was google forms, since a friend of mine did in the past. However, after implementing some test code from a tutorial, I can't get responses through and not receiving any data despite following a tutorial. Instead it complains about 401 unauthorized error. Heres some code :
private string target_url =
"https://docs.google.com/forms/myForm/formResponse";
private IEnumerator RoutineSendActual()
{
WWWForm form = new WWWForm();
form.AddField( "entry.689820410", "code test" );
var www = new WWW( target_url, form.data );
Debug.Log( www.url );
yield return www;
Debug.Log( "sent " + www.error + " " + www.isDone + " " + www.responseHeaders);
foreach ( var responseKey in www.responseHeaders.Keys )
{
Debug.Log( responseKey + "|" + www.responseHeaders[responseKey] );
}
Debug.Log( www.text );
yield break;
}
Instead www.error is giving me 401 unauthorized error. I've followed the following tutorial https://www.youtube.com/watch?v=z9b5aRfrz7M
and the following questions has it working How to add value on another section in google form in Unity3d
but for me, I get 401 unauthorized. I even made sure the Forms and spreadsheet where made public and editable to everyone (which i'm worried about because could hack the data).
Also, should I instead be looking into Google Apps Script to act as an intermediary between unity web request and the actual form.

{ Note: I'm copying this from another question I answered, but I considered it an answer to my own question }
Unfortunately, it seems that this method of posting info to google forms is unreliable, as it's not well documented at all progress here is from looking at the html page code. I've followed the same tutorial and also having trouble.
But fortunately, I've found a better solution that gives you better control and access to google forms.
https://developers.google.com/apps-script/
https://developers.google.com/apps-script/reference/forms/
Instead of guessing the post API, using Google Apps Script, you can setup and deploy a web app from googles own servers in javascript, and interface with forms directly. From there you just have to design JSON payload. I got it working already using Postman to simulate the web request that will be sent from unity.

Check that the Google Form has public permissions. Maybe you have set them to a single user or to a single organization.

Related

How do I handle the need for CSFR token when using SAP Cloud SDK?

I am using the SAP Cloud SDK for Java to do CRUD on the SalesOrder APIs in S/4. Everything works well in that I can carry out these actions from Postman. However, these requests from Postman only work if I include a pre-request script to get a csrf token as outlined in this blog post
If I run the requests without the pre-request script outlined in the blog post, I get a '403 Forbidden'. As I said it works from Postman, but I would like to understand how this should be handled without the need for this script, for example if I was making a request from another application. Does the SDK allow me to handle this from the application code somehow. Maybe I am missing something.
Thanks for your time.
EDIT:
I am not making requests to the S/4 directly from Postman. I have an app deployed which is using the Cloud SDK to make the requests to S/4. It works if I use the pre-request script to fetch the CSFR token and attach it to the request before I send it, but 403 if I don't. So, if we imagine I am not using Postman but some ui somewhere to fill a form and send this request my understanding is that I shouldn't, as you suggested, have to worry about this token, that my service in the middle which uses the SDK and the VDM should handle this for me. This is what I am struggling to understand.
This is the servlet code:
#Override
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
String body = IOUtils.toString(request.getReader());
JSONObject so = new JSONObject(body);
String distributionChannel = so.get("DistributionChannel").toString();
String salesOrderType = so.get("SalesOrderType").toString();
String salesOrganization = so.get("SalesOrganization").toString();
String soldToParty = so.get("SoldToParty").toString();
String organizationDivision = so.get("OrganizationDivision").toString();
String material = so.get("Material").toString();
String requestedQuantityUnit = so.get("RequestedQuantityUnit").toString();
SalesOrderItem salesOrderItem = SalesOrderItem.builder()
.material(material)
.requestedQuantityUnit(requestedQuantityUnit).build();
SalesOrder salesOrder = SalesOrder.builder()
.salesOrderType(salesOrderType)
.distributionChannel(distributionChannel)
.salesOrganization(salesOrganization)
.soldToParty(soldToParty)
.organizationDivision(organizationDivision)
.item(salesOrderItem)
.build();
try {
final ErpHttpDestination destination = DestinationAccessor.getDestination(DESTINATION_NAME).asHttp()
.decorate(DefaultErpHttpDestination::new);
final SalesOrder storedSalesOrder = new CreateSalesOrderCommand(destination, new DefaultSalesOrderService(),
salesOrder).execute();
response.setStatus(HttpServletResponse.SC_CREATED);
response.setContentType("application/json");
response.getWriter().write(new Gson().toJson(storedSalesOrder));
logger.info("Succeeded to CREATE {} sales order", storedSalesOrder);
} catch (final Exception e) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
logger.error(e.getMessage(), e);
logger.error("Failed to CREATE sales order", e);
}
}
And the CreateSalesOrder command:
public SalesOrder execute() {
return ResilienceDecorator.executeSupplier(this::run, myResilienceConfig);
}
protected SalesOrder run() {
try {
return salesOrderService.createSalesOrder(salesOrder).execute(destination);
} catch (final ODataException e) {
throw new ResilienceRuntimeException(e);
}
}
I am using the version 3.16.1 of the SDK and have set logging level to DEBUG for the SDK in the manifest:
SET_LOGGING_LEVEL: '{ROOT: INFO, com.sap.cloud.sdk: DEBUG}'
and logging level to DEBUG in logback
If I remove the pre-request script from the request and send it I get the 403 response and logs shows the following messages:
"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationListener","thread":"http-nio-0.0.0.0-8080-exec-4","level":"DEBUG","categories":[],"msg":"Reading
user principal"
"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationListener","thread":"http-nio-0.0.0.0-8080-exec-4","level":"DEBUG","categories":[],"msg":"Destroying Authorization as it is end of request." }
"logger":"com.sap.cloud.sdk.service.prov.api.security.AuthorizationService","thread":"http-nio-0.0.0.0-8080-exec-4","level":"DEBUG","categories":[],"msg":"Destroying Authorization JWT Token." }
As the other answers focus on the app to S/4 communication and you adjusted your question to make clear that you mean the User (e.g. Postman) to app communication I'll provide some additional information.
As mentioned by the other answers the CSRF handling to the S/4 system (or any OData endpoint) is automatically handled on side of the OData VDM.
What you are now encountering is the secure default configuration of the SAP Cloud SDK Maven Archetypes, which have the RestCsrfPreventionFilter activated by default.
This filter automatically protects all non-GET endpoints from CSRF by requiring you to fetch a CSRF Token prior to your request which you then provide.
This is completely unrelated to the OData VDM call to the S/4 system in the background.
To remedy your problems there are now three next steps:
Use a GET endpoint instead of POST
Probably only as a temporary workaround
Remove the RestCsrfPreventionFilter temporarily from your web.xml
This should not be done for productive uses, but might make your life in the initial evaluation easier.
"Live with it"
As this is a commonly used pattern to protect your application against CSRF it's advised to keep the filter in place and do the CSRF-Token "flow" as required.
Further Reading
OWASP description of CSRF: https://owasp.org/www-community/attacks/csrf
OWASP cheat sheet on CSRF protection (linked to the approach used by the filter): https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#use-of-custom-request-headers
JavaDoc of the RestCsrfPreventionFilter: https://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/catalina/filters/RestCsrfPreventionFilter.html
Inkers
You're correct, with an API tool like Postman you have to make a HEAD request first to get a CSRF token.
However, in Cloud SDK for Java, we take care of getting and refreshing CSRF token for you when you're making any CRUD request.
Here's an example of reading a Saler Oder item and updating it afterward:
// Create a new sales order item
SalesOrderItem item = new SalesOrderItem();
item.setSalesOrder(SALES_ORDER);
item.setNetAmount(new BigDecimal(NET_VALUE));
item = service.createSalesOrderItem(item).execute(destination).getResponseEntity().get();
// Modify it with a PATCH update to 9000 net value
item.setNetAmount(new BigDecimal(NET_VALUE_UPDATED));
ModificationResponse<SalesOrderItem> response = service.updateSalesOrderItem(item).modifyingEntity().execute(destination);
Try it and let up know if it works fine for you. We're happy to assist if you'll encounter any difficulties.
The SDK makes an attempt to fetch a CSRF token automatically within execute(destination). This happens before issuing the actual request. If the attempt is successful the token will be included in the request. If not, the request will be send regardless.
Please increase the log level to debug for all com.sap.cloud.sdk packages if you think this is not happening correctly. Also it would be great to see the actual HTTP requests that go in and out which you can enable by setting the log level of org.apache.http.wire also to debug. Then attach the stack trace here together with the SDK version you are using and the exact code you are invoking.

Throwing 404 error with the authoriseUrl generated from GetRefreshToken

I am trying to create a sample project which consumes DFP services.
Till now :
Still in authentication part :
a) Successfully created clientId, while creating new Client Id have checked installed apps options in the Google developer console.
b) Added the clientId, clientSecret to ads.properties .
Note : Took dfp-axis-jars-and-examples-2.2.0.tar and making my changes in it[by importing project in IDE].
c) I ran into trouble while trying to paste the authoriseUrl in the browser after running GetRefreshToken.
Please find below the error :
"The redirect URI in the request: urn:ietf:wg:oauth:2.0:oob can only
be used by a Client ID for native application. It is not allowed for
the 'WEB' client type. You can create a Client ID for native
application in the Credentials section of the Google Developers
Console."
I believe I have selected the wrong applicationType while creating new ClientId .
Appreciate inputs .
The above answer did not make much sense at first glance but, I have resolved this issue by simply setting a value for redirectUri. So inside of my GetRefreshToken.php file
$redirectUri = "http://same_url_as_the_one_listed_in_console";
After closing looking at the authoriseUrl, it seemed I was sending the wrong redirectUrl.
I forgot to copy paste the redirectUrl generated from the creation of client id in the GetRefreshToken callback property:
private static final String CALLBACK_URL = "https://www.example.com/oauth2callback";//"urn:ietf:wg:oauth:2.0:oob";
No error now :).

Fitbit OAuth 2.0 and Unity Project (RestSharp as well)

I've been trying to get OAuth 2.0 to work correctly. I have managed to make the url that that will do the "deny/allow" for my app by opening a webpage with just
Application.OpenUrl(uri.ToString());
The problem is that I have no idea how to get the redirect and the auth token from the page if the user hits allow. When you hit allow, right now nothing happens it just sits on the page. Checking Networking in chrome debug does have the redirect and token there but it never actually sends it..
I was recommended to use RestSharp but I again have no idea how to use it with Unity as there are lot of resources for Android/iOS PC etc. but I can't get any of them to work for this Unity project...
var client = new RestClient("https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=*clientID*&redirect_uri=http%3A%2F%2FfitRPGcallback&scope=activity%20profile%20sleep%20social");
Debug.Log("client made");
var request = new RestRequest(Method.POST);
request.Resource = ("profile%20sleep%20social");
client.ExecuteAsync(request, response => { Debug.Log("response is : " + response.Content);});
Application.OpenURL(client.BaseUrl.ToString());
In the URL I do have the correct clientID in there as well just not sure what I can and can't show for security reasons etc.
Biggest problem is just having no idea how to get the return value from the webpage after the user hits allow/deny...
Any insights would be super super appreciated cause I just want to start making the actual game but there's not as much point if I can't get this data...
So, I'm posting on a few other applicable questions as well since I have finally figured out my answer.
Unfortunately I'm not using REST so that part is still up in the air BUT I did get it to work with just Fitbit, Unity and a Webview plugin (you will need a webview OR a way to get the initial code back from your first OAuth2 call)
You can find steps here.
http://technicalartistry.blogspot.nl/2015/07/oauth2-unity-and-month-of-cursing.html
EDIT:
So I had to change how I did it because Fitbit changed their ToS where we are no longer allowed to use Webview based Authenticators (which is what I was using in the above blogpost.)
Give this next post a look for how to make an Android Plugin that will grab the Accesstoken from Fitbit's OAuth. This is a FREE way to do it since you make it yourself and it's ezmode :)
http://technicalartistry.blogspot.ca/2016/01/fitbit-unity-oauth-2-and-native.html

Sitelock .unity3d

I had done some modifications to unity games (like infinite ammo, player invincible) using .net reflector thru reflexil plugin & hosted on my domain.
Can I sitelock .unity3d files to my domain & how
You should be able to check the domain in the code (best result check in the first scene) so that if it not on your chosen URL then then can do a different action (i.e. redirect, display a message)
This manual page
should show what you need and
Application.ExternalEval("if(document.location.host != 'YOURDOMAIN.COM') { document.location='WHERE_TO_REDIRECT_YOUR_USER; }");
will push them back to your site each time. You could increase this to report back to you as well and you would have a log of sites hosting incorrectly it required.
I wrote a script that will "lock your Unity3D game to a specific list of websites."
An updated version is also available on GitHub and also, identically on Bitbucket
From the readme.md
A C# script for Unity 3D that prevents your Web Player project from running on
websites that are not authorised.

You may also need to check that the game is not embedded in another site using an iframe.
When dealing with frames you can't access a page from a different domain.
This actually means we don't even need to check the host of the outer page, because if we can even access it then we know it's from the same domain anyway.
So this will redirect someone if your page is not being embedded in a page with the same host:
Application.ExternalEval(
"if(parent && parent.document == undefined)
{
window.top.location.replace('http://goo.gl/9ulDD');
}"
);
If you really do need to check the exact URL (for example, you're hosting the page on foo.com and you want bar.com to be able to embed your content) then you can check the document.referrer, which is set to the URL of the page that includes the frame:
Application.ExternalEval(
"if (parent)
{
var pathParts = document.referrer.split('/');
if (pathParts[2] != '" + locktoSite + "')
window.top.location.replace('http://goo.gl/9ulDD');
}"
);

Securing REST Service using a simple Token

I have a drop-down in my application for which i populate the data using an Ajax call as shown below. This works fine and my Web API URL is "http://example.com/Service.svc/json/getairports"
$(function () {
$.getJSON("http://example.com/Service.svc/json/getairports", {}, function (data) {
$("#airport-departure-modal, #destination-airport-modal").html("")
$.each(data, function (index, element) {
$("#airport-departure-modal, #destination-airport-modal").append("<option id=" + element["AirportCode"] + ">" + element["AirportName"] + "</option>");
});
});
});
But my worry is security since anyone can view this URL using view source or developer tools. So i wanted to bring in a token and pass it to the service like "http://example.com/Service.svc/json/getairports?token=SECUREKEY" but i wonder how this can solve the problem since secure key also visible in the view source. So my question is how can i keep the secure key invisible in the client side or dynamically passed only when the ajax call is initiated?
Just for information, i will be using HTTPS in production so that sniffing over the wire is taken care. Also not that, this service is going to be used only within the same application though Web API service might be hosted on a separate node.
Kindly advise if there might be some alternative but simple solution for the above scenario. i am aware of other advanced mechanisms such as OAuth,HMAC, Amazon S3, etc. But i just want to have a simple solution.
What exactly are you trying to solve? Do you want to prevent a user from calling your API programmatically? If your browser can get something, so can a user with the power of view-source - so that's a fruitless effort. Really though, it sounds like you want to prevent CSRF.
This answer should be helpful.