After a lot of reading and Googling it seems I have made a complete setup for Google Cloud Messaging to send push-notifications. My missing link is the Reference_Ids that I must use to target apps. I have created a project and also added my apps to it.
When I send a push-request to GCM I get the following response:
{"multicast_id":7952352701122753715,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Alt-Svc: quic=":443"; ma=2592000; v="35,34"
Vary: Accept-Encoding
Transfer-Encoding: chunked
Accept-Ranges: none
Cache-Control: max-age=0, private
Date: Wed, 21 Dec 2016 16:12:43 GMT
Server: GSE
Content-Type: application/json; charset=UTF-8
Expires: Wed, 21 Dec 2016 16:12:43 GMT
}
And the error reads "InvalidRegistration".
So my questions are:
Where do I find my registration Ids?
And as a related follow-up question, where do I find Registration_Ids for everyone using an app or a specific group or user?
BTW: I found a related question, but it does not seem to have an answer to as where to find these Ids. StackOverflow post.
InvalidRegistration means that the registration token (registration id) you used is invalid (doesn't exist, wrong format):
Check the format of the registration token you pass to the server. Make sure it matches the registration token the client app receives from registering with Firebase Notifications. Do not truncate or add additional characters.
Make sure that you are using the correct and corresponding registration token to the device you intend to send the message to. For testing, I would suggest to make use of the Firebase Console too, so that you can see if the error still occurs from there.
For Android, you can retrieve the registration token by calling:
FirebaseInstanceID.getToken()
You may then choose to store the token to your App Server.
You should use Api server_key .
Go to firebase console -> click on your project -> click on gear icon -> project_setting -> cloud_messaging
Related
I am trying to get the OAuth2 Playground to return a receipt for a purchase made on our flutter app, however I've been unable to successfully get it to work.
I have all the required information
ProjectID = com.myorg.myapp
ProductID = myapp.funds.five
PurchaseToken = TokenValueGoesHere
I authorize, get to the point of "Configure request to API", I fill out the appropriate url
https://androidpublisher.googleapis.com/androidpublisher/v3/applications/[ProjectID]/purchases/products/[ProductID]/tokens/[PurchaseToken]
however, the playground returns with
HTTP/1.1 403 Forbidden
Content-length: 423
X-xss-protection: 0
X-content-type-options: nosniff
Transfer-encoding: chunked
Vary: Origin, X-Origin, Referer
Server: ESF
-content-encoding: gzip
Cache-control: private
Date: Thu, 26 Jan 2023 12:52:26 GMT
X-frame-options: SAMEORIGIN
Alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Content-type: application/json; charset=UTF-8
{
"error": {
"message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console.",
"code": 403,
"errors": [
{
"reason": "projectNotLinked",
"message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console.",
"domain": "androidpublisher"
}
]
}
}
We double and triple checked to ensure that the API is linked. I've created new products after the linking because I've seen others have to do that. We believe the service account has the correct permissions.
What am I missing and how do I fix this so I can verify the receipt of a purchase?
Alright so the project WAS linked properly and the error was at best vague as to what the actual problem was, in being that the OAUTH credential I was providing the playground wasn't sufficient to pull the information.
I ended up solving the issue in code.
#GetMapping("/verifyPurchase/app/appName={appName}&productId={productId}&purchaseToken={purchaseToken}")
public boolean isPurchaseSuccessful(#PathVariable(value = "appName") String appName,
#PathVariable(value = "productId") String productId,
#PathVariable(value = "purchaseToken") String purchaseToken) throws GeneralSecurityException, IOException {
// Build service account credentials, important to include the scope which you can get from googles documentation
GoogleCredentials serviceAccountCredentials =
ServiceAccountCredentials.fromStream(new FileInputStream("src/main/resources/auth.json")) // Service Account Credentials json file from Google cloud
.createScoped(Collections.singleton("https://www.googleapis.com/auth/androidpublisher"));
// Android publisher object, slightly older version but does operate as of 2023-Jan-26
AndroidPublisher androidPublisher = new AndroidPublisher.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
JacksonFactory.getDefaultInstance(),
new HttpCredentialsAdapter(serviceAccountCredentials)
).setApplicationName(appName).build(); // Give it the application to build the publisher for.
// At this point we're authorized. We can pull down the receipt from the API, and provide the publisher
// with the appropriate product information and purchase token that we got when we made the purchase from the
// flutter application side of things.
var receipt = androidPublisher.purchases().products().get(appName, productId, purchaseToken).execute();
// return if the purchase was successful, may need error handling here testing to come.
return PURCHASED.equals(receipt.getPurchaseState());
}
I am trying to connect to a WebSocket server that my Android device connects to from an app. I captured the packets on my Android device, and the initial request headers look like this:
GET / HTTP/1.1
Pragma: no-cache
Cache-Control: no-cache
Host: example.com:80
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ysWaBflPV9EmRaB1JpPMOQ==
Origin: http://example.com:80
Sec-WebSocket-Protocol: default-protocol
Sec-WebSocket-Extensions:
Sec-WebSocket-Version: 13
The response from the server looks like this:
HTTP/1.1 101 Switching Protocols
Date: Wed, 31 Jan 2018 02:37:20 GMT
Connection: upgrade
Set-Cookie: AWSALB=0yaRd5HOPlZSITp+bcXoZoIn/7YOOqE9o4/t/8b3kw2PTxooflm/85w+1JfudEE0Cwb1BUkWPV+t4kOnEm4FbLSWwMMFp8URbblZKj0a0kd0xB+glbLBHWxc/TPW; Expires=Wed, 07 Feb 2018 02:37:20 GMT; Path=/
Server: nginx/1.12.1
Upgrade: websocket
Sec-WebSocket-Accept: bj5wLF8vmyDrA7pqEgbHKbxqQSU=
Then, some communication begins, with lots of unrecognizable characters and some clear words in the messages. I don't have much experience with WebSockets, but I assume it is some form of compression.
I was able to send an identical request to this server using the ws module in Node.js, and I got an identical response to the one above. One notable difference was that when I set the protocol header to default-protocol, I received an error saying "Server sent no subprotocol". Without using this header, I still got the same response.
After the initial response, however, I did not receive any more messages, even though I did on my Android device. After about 30 seconds, the connection closes with code 1006 and no further information.
I tested the same request with curl and received the same headers back, but it also closed after about 30 seconds saying:
"Empty reply from server"
So my main question is obviously: What is going wrong, and how can I fix it?
More specifically, I am wondering if anyone with WebSocket experience knows if it is a problem with my client, or with the server itself.
It is possible that the server is authenticating me in some way on my Android device, but the headers that I captured are not revealing anything about that. Is it ever customary to authenticate a connection with a later message in the client-server communications? Is it possible that a separate HTTP request is authenticating me for this WebSocket server? All of these things seem unlikely to me since I found no other packets with anything related to auth requests. It seems much more likely that there is something wrong with the messages being sent.
Background:
I am experiencing a very confusing behaviour with android Webviews in API 21 and up when testing in real devices.
I have a local HTML5 application (inside assets folder) with the following functionality
Login (2 steps authentication).
Show a list of items depending on the authentication.
The problem:
After doing the login requests, the server returns a cookie with the session. This cookie is not stored in the Webview when using real devices with API 21 or up. If I use emulators (Genymotion in this case), the cookies are properly stored.
More information:
The request to do the auth has the following headers:
POST http://myServer/j_spring_security_check HTTP/1.1
Proxy-Connection: keep-alive
Content-Length: 101
access-control-allow-origin: *
accept: application/json
access-control-allow-credentials: true
User-Agent: Framework/1.5.0 (Linux; U; Android 6.0.1; Nexus 5X Build/MMB29Q) App/0.1.1
Origin: file://
content-type: application/x-www-form-urlencoded
Accept-Language: en-US
X-Requested-With: app.package
Host: myServer
With the following response:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=4D169E8656DBEDFFA4D17FE8D436A5BA; Expires=Fri, 19-Feb-2016 14:27:55 GMT; Path=/; HttpOnly
Content-Type: application/json;charset=UTF-8
Content-Length: 43
Date: Fri, 19 Feb 2016 14:17:55 GMT
The cookie is not stored in devices with API 21 or more. Same request/response works fine in the rest of devices + all the emulators
Clarification:
This flags are enabled inside the app:
android.webkit.CookieManager.setAcceptFileSchemeCookies(true);
(Before CookieManager or webview is instantiated, as documentation says)
if(VERSION.SDK_INT >= 21) {
CookieManager.getInstance().setAcceptThirdPartyCookies(this.nativeWebView, true);
}
If after doing the authentication, I access the cookies datastore and
check the "hasCookies" method, I get false.
The two step auth service actually calls 3 different paths from the same endpoints. None of the cookies that the response that generate this services are stored. I don't know if this is relevant or not.
When doing simple authentication (to a different server), cookies are stored properly in all the devices emulators.
I am using Angular 1.5
I am aware that the service is using http instead of https. That will be solved in the future.
I get no error message in the consoles.
Questions:
Is there any internal security measure in the webviews that blocks the storage of the cookies? Why does it work on emulators (that are rooted devices) and not in real devices? This really bugs me.
If the network request is done using window.fetch you may need to add:
fetch('/something', { credentials: 'same-origin' }) // or 'include'
On chromium, window.fetch has the credentials flag set by default to 'omit' and no cookies are stored into the cookie storage. More details of this bug here: https://bugs.chromium.org/p/chromium/issues/detail?id=477523
I'm trying to get logged in soundcloud by using their API. I manage to got a code with a success response from /connect but when I try to get a token, I end up with a 401 error. Here is what I send :
POST /oauth2/token HTTP/1.1
Host: api.soundcloud.com
Content-Type: application/x-form-urlencoded
Cache-Control: no-cache
Postman-Token: 255c7769-0f65-5c40-27bc-9429ea8c38ea
client_id=<my client id>&
client_secret=<my secret id>&
redirect_uri=http%3A%2F%2Fmywebsite&
grant_type=authorization_code&
code=a99cfc85c0e46235d7fdb9ca74b7dddd%23
Does anyone knows what I miss here to get a working request ?
It turned out I was not calling /connect correctly, this is the correct way :
https://soundcloud.com/connect?
client_id=<my client id>
&redirect_uri=<my redirect url>
&response_type=token
&scope=non-expiring
&display=popup
With response_type=token, we directly receive an access token so we don't need to call /oauth2/token then.
I am currently following the instructions given here for cross-client validation in an Android-GAE app, so my users can give the Python backend off-line access to the G+ APIs on their behalf. That documentation also directs me here, which is really where I am having the trouble. I have successfully obtained an authorization code and sent it to the backend, but when the backend tries to exchange that code for access and refresh tokens I get "error:invalid_grant" with "error description: invalid code". To illustrate, when I copy the code and other needed info into a curl, the output is as follows:
curl -d "code=4/uG6moqbHvs9hgfG1HmEfT39zT1j0.Un-H_36OXGUWmmS0T3UFEsMwQpwOhwI&client_secret={MY_SECRET}&client_id={MY_WEB_CLIENT_ID}&grant_type=authorization_code" https://accounts.google.com/o/oauth2/token -v
* Adding handle: conn: 0x7fc359803a00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fc359803a00) send_pipe: 1, recv_pipe: 0
* About to connect() to accounts.google.com port 443 (#0)
* Trying 74.125.28.84...
* Connected to accounts.google.com (74.125.28.84) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_RC4_128_SHA
* Server certificate: accounts.google.com
* Server certificate: Google Internet Authority G2
* Server certificate: GeoTrust Global CA
* Server certificate: Equifax Secure Certificate Authority
> POST /o/oauth2/token HTTP/1.1
> User-Agent: curl/7.30.0
> Host: accounts.google.com
> Accept: */*
> Content-Length: 219
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 219 out of 219 bytes
< HTTP/1.1 400 Bad Request
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Fri, 01 Jan 1990 00:00:00 GMT
< Date: Sun, 12 Jan 2014 16:38:42 GMT
< Content-Type: application/json
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
* Server GSE is not blacklisted
< Server: GSE
< Alternate-Protocol: 443:quic
< Transfer-Encoding: chunked
<
{
"error" : "invalid_grant",
"error_description" : "Invalid code.[Email: \n\nToken Record:\nToken: \"4/uG6moqbHvs9hgfG1HmEfT39zT1j0.Un-H_36OXGUWmmS0T3UFEsMwQpwOhwI\"\nIssueDomain: \"206701529154-7d35h3g4a22aef8d78p6up1cdiq3pl4d.apps.googleusercontent.com\"\nIssueTimeSec: 1389544361\nExpirationTime: 1389544961\nTokenUsage: 3\nScope: \"https://www.googleapis.com/auth/plus.login\"\nScope: \"https://www.googleapis.com/auth/userinfo.profile\"\nScope: \"https://www.googleapis.com/auth/userinfo.email\"\nScope: \"https://www.googleapis.com/auth/plus.moments.write\"\nScope: \"https://www.googleapis.com/auth/plus.me\"\nScope: \"https://www.googleapis.com/auth/plus.profile.agerange.read\"\nScope: \"https://www.googleapis.com/auth/plus.profile.language.read\"\nScope: \"https://www.googleapis.com/auth/plus.circles.members.read\"\nServiceInfo {\n ServiceId: 226\n Info <\n [security_lso_auth_oauth2.EarlyIssuedTokenProto] <\n auto_approved: false\n access_token: \"ya29.1.AADtN_UyJ2wiWB7lh7btUiCwUyHzix_DrubjdSsTxA6drG-ZccPWaeZJa31sebPnRCHiFw\"\n refresh_token: \"1/ImSjHYLkFcBOwsCezBg2dKpcqko9by3nTIh_k33ZMds\"\n >\n >\n}\nRevoked: true\nAuthorizedBy: 0x866419b291\nOAuthCallbackUrl: \"urn:accounts.google.com:api_auth:programmatic:virtual_redirect_uri\"\nOfflineAccess: true\nRevokeOnPasswordChange: true\nClientManagedRevocation: false\nInBundle: true\n]"
* Connection #0 to host accounts.google.com left intact
}%
I'm having a lot of trouble making sense of this error message to determine why my authorization code might be invalid. I did notice that in the explorer logs, all logs are time-stamped an hour in the past (so when I post a log at 11AM, it appears in the explorer with a timestamp of 10AM). Could a timezone discrepancy somehow be causing the oauth2 servers to think I am submitting authorization codes that are too old? I already tried changing my system time to match the log time, to no avail. Does anyone know what could be causing my authorization code to be invalid? I have searched high and low for hours.
Shortly after posting this question, I discovered the answer myself. Posting it here in case others run into the same problem. I noticed that a similar error was occurring in Google API Invalid grant on one-time auth code from GoogleAuthUtil.getToken, and the reason was that the developer needed to request refresh/access tokens multiple times within the span of 10 minutes (which is the expiration duration of the one-time authorization code). Inspecting my explorer logs again, I saw that I have a bug in my android code causing my cross-client validation code to be run twice in rapid succession instead of only once like it's supposed to. The first time it's run, the refresh and access tokens are returned correctly. The second time, the error is generated.
In retrospect, the "revoked = True" part of the error message should have pointed me to the answer.