Vanar 2 Posted September 13 I always received FDeviceToken from the request https://iid.googleapis.com/iid/v1:batchImport HTTP HEADERS: Content-Type: application/json Authorization: key=YOUR_SERVER_KEY BODY: { "application": "com.company.app", "sandbox":true, "apns_tokens":[ "7c6811bfa1e89c739c5862122aa7ab68fc4972dea7372242f74276a5326f...." ] } Answer: { "results": [ { "registration_token": "ejXQlECjCeI:APA91bE7oaUhaFnGyl77lFrySdEaWxocM0oj81uNezACX1wsZXiTyL4OYo5ssvFjjWYpFymMVyqBccboVcwTTW2rvykOmV_CABDM7rTIRCiJFl_9ngf7SrDSYoFouwNj69JSwlH.....", "apns_token": "7c6811bfa1e89c739c5862122aa7ab68fc4972dea7372242f74276a5326f....", "status": "OK" } ] } But recently this request stopped working (authorization required). I get the answer: { "error": "Not authenticated or unauthorized"} How to solve this problem? How to rewrite the query? Most likely it is related to this Share this post Link to post
John van de Waeter 7 Posted October 14 Hi Vanar, I'm facing the same problem... did you find a solution? cheers, John Share this post Link to post
Dave Nottage 557 Posted October 14 See this documentation. Authorization is now via an OAuth2 token - there is a link in the documentation to details about methods for obtaining the token. Share this post Link to post
John van de Waeter 7 Posted October 15 I wonder why Google and Apple are making things more difficult.... :/ Share this post Link to post
John van de Waeter 7 Posted October 16 On 10/14/2024 at 9:37 PM, Dave Nottage said: See this documentation. Authorization is now via an OAuth2 token - there is a link in the documentation to details about methods for obtaining the token. Dave, I'm trying Kastri now. Again 🙂 One thing I must do is download a copy of firebase.zip (SDK for iOS) from github. But both 11.2.0 as 11.3.0 give me an error when I try to unzip them: Invalid Zip-file. Tried on both win10 and win11. I tried to ask a question in the issues section of this github-repo, but my question does not appear in the discussion. Dunno why, no error reported. So I'm trying here: do you know an alternative to download this firebase.zip? Thanks, John Share this post Link to post
Dave Nottage 557 Posted October 16 36 minutes ago, John van de Waeter said: So I'm trying here: do you know an alternative to download this firebase.zip? There's this link at this page, however it might just be a redirect to the latest version (11.3.0) I notice a huge difference in size between 11.2.0 and 11.3.0 - 319MB vs 612MB!! Share this post Link to post
John van de Waeter 7 Posted October 16 Yep, lots of other code in this file. But it contains the original firebase.zip, which in turn has the same problem.... Share this post Link to post
Dave Nottage 557 Posted October 16 6 hours ago, John van de Waeter said: Yep, lots of other code in this file. But it contains the original firebase.zip, which in turn has the same problem.... Both unzip OK on macOS, so I tried to "re-zip" the 11.2.0 version on Windows, but that causes Explorer to crash 😞 I've asked a question about it in their Slack workspace, since all the other reporting mechanisms don't seem to cover this category. Share this post Link to post
Vanar 2 Posted October 18 On 10/14/2024 at 5:28 PM, John van de Waeter said: Hi Vanar, I'm facing the same problem... did you find a solution? cheers, John Hi, John I didn't find a normal solution I temporarily solved the problem like this: They give me a ready-made authorization key: ya29.c.c0ASRK0Ga_4Z1QIYsqufOCVdT ...., created on the server using PHP And I slip it into my request ... Share this post Link to post
John van de Waeter 7 Posted October 18 12 minutes ago, Vanar said: Hi, John I didn't find a normal solution I temporarily solved the problem like this: They give me a ready-made authorization key: ya29.c.c0ASRK0Ga_4Z1QIYsqufOCVdT ...., created on the server using PHP And I slip it into my request ... Lol.... I too had a hard fight to send those notifications to the v1-google fcm server.... I never realized that the converting part (apns->fcm) also required this new approach... Having apps online that suddenly don't receive pn's anymore forces one to be creative.... I'm now trying the Kastri-path. Share this post Link to post
Vladsrb 2 Posted October 18 Hi, i was facing with the same problem... This is my workable solution, in short 🙂 uses System.Net.HttpClient, JOSE.Core.Builder, JOSE.Core.JWT, JOSE.Core.JWA, JOSE.Types.Bytes, JOSE.Types.JSON, JOSE.Signing.RSA, JOSE.Core.JWK JOSE is taken from https://github.com/paolo-rossi/delphi-jose-jwt Info: FGoogleProjectID : is your project ID like 'name-95dc3e3' (console.cloud.google.com) FGoogleServiceEmail: like 'firebase-adminsdk-9tm7h@XXXXXXXX.iam.gserviceaccount.com' FGoogleServicePrivateKey: '-----BEGIN PRIVATE KEY-----...' taken from the JSON file when you create a key for a Google service account (you need to remove the \n characters from the key) function CreateGoogleTokenString: String; begin Result := ''; var JWToken: TJWT := TJWT.Create(TJWTClaims); try try JWToken.Claims.Issuer := FGoogleServiceEmail; JWToken.Claims.SetClaim('scope', 'https://www.googleapis.com/auth/firebase.messaging'); JWToken.Claims.Audience :='https://oauth2.googleapis.com/token'; JWToken.Claims.IssuedAt := Now; JWToken.Claims.Expiration := IncMinute(Now, 60); // not more than 60 var AJWKKey: TJWK := TJWK.Create(FGoogleServicePrivateKey); var LSignature: TJOSEBytes := TJOSE.SerializeCompact(AJWKKey, TJOSEAlgorithmId.RS256, JWToken); Result := LSignature.AsString; except on e: exception do raise Exception.Create('Error creating Google Token String: ' + E.Message); end; finally FreeAndNil(JWToken); end; end; function GetOAuthToken: String; var LResponse: IHTTPResponse; LFormUrlencoded: TStringList; LRequest: THTTPClient; begin Result := ''; LRequest := THTTPClient.Create; LFormUrlEncoded := TStringList.Create; try try var AToken: String := GCCreateGoogleTokenString; LFormUrlEncoded.Add('grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer'); LFormUrlEncoded.Add('assertion=' + AToken); LResponse := LRequest.Post('https://oauth2.googleapis.com/token', LFormUrlencoded); if LResponse.StatusCode = 200 then begin Result := LResponse.ContentAsString(); var JSONObj: TJSONObject := TJSONObject.ParseJSONValue(Result) as TJSONObject; try if Assigned(JSONObj) then Result := JSONObj.GetValue<string>('access_token'); finally JSONObj.Free; end; end; except on e: exception do begin Result := ''; raise Exception.Create('Error getting Google Token: ' + E.Message); end; end; finally LFormUrlencoded.Free; LRequest.Free; end; end; sending a push message with RESTClient: function CreateBodyMessageJSON(AFBToken: String): String; // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Notification begin var AJSONMain: TJSONObject := TJSONObject.Create; try var AJSONMessage: TJSONObject := TJSONObject.Create; AJSONMessage.AddPair('token', AFBToken); // for all var AJSONNotification: TJSONObject := TJSONObject.Create; AJSONNotification.AddPair('body', FBody); AJSONNotification.AddPair('title', FTitle); AJSONMessage.AddPair('notification', AJSONNotification); // for Android var AJSONNotification1: TJSONObject := TJSONObject.Create; AJSONNotification1.AddPair('sound', 'default'); // AJSONNotification1.AddPair('notification_count', TJSONNumber.Create(1)); // if needed var AJSONAndroid: TJSONObject := TJSONObject.Create; AJSONAndroid.AddPair('notification', AJSONNotification1); AJSONMessage.AddPair('android', AJSONAndroid); // for IOS var AJSONAps: TJSONObject := TJSONObject.Create; AJSONAps.AddPair('sound', 'default'); AJSONAps.AddPair('badge', TJSONNumber.Create(1)); // Show badge on app icon on IOS var AJSONPayload: TJSONObject := TJSONObject.Create; AJSONPayload.AddPair('aps', AJSONAps); var AJSONApns: TJSONObject := TJSONObject.Create; AJSONApns.AddPair('payload', AJSONPayload); AJSONMessage.AddPair('apns', AJSONApns); AJSONMain.AddPair('message', AJSONMessage); Result := AJSONMain.ToJSON; finally AJSONMain.Free; end; end; RESTClientPushmessage.BaseURL := 'https://fcm.googleapis.com/v1/projects/' + FGoogleProjectID + '/messages:send'; RESTRequestPusMessage.Params.ParameterByName('Authorization').Value := 'Bearer ' + GCGetOAuthToken; // pkHTTPHEADER RESTRequestPusMessage.Params.ParameterByName('Content-Type').Value := 'application/json' // pkHTTPHEADER RESTRequestPusMessage.Params.ParameterByName('body').Value := CreateBodyMessageJSON(AFireBaseToken); // pkREQUESTBODY RESTRequestPusMessage.Execute; If i miss something let me know... For reading FB token on IOS i use: Firebase SDK for IOS 6.28 (working on Delphi 12.2) Share this post Link to post
Dave Nottage 557 Posted October 18 On 10/16/2024 at 8:54 PM, John van de Waeter said: But both 11.2.0 as 11.3.0 give me an error when I try to unzip them: Invalid Zip-file. Tried on both win10 and win11. A follow-up to this: I have contacted their support team, who requested that I provide a video demonstrating the problem 🙄. I have since replied, asking them if they really are incapable of doing two simple steps (Using Windows: download, and try to open) to replicate it themselves. Share this post Link to post
John van de Waeter 7 Posted October 19 (edited) 17 hours ago, Dave Nottage said: (Using Windows: download, and try to open) to replicate it themselves. I used WinZip to unzip the archive, which allowed me to skip files with bad filenames. I figured if those filenames are not allowed in Windows, these files are probably not used anyway. It worked. ps, Dave, if I have questions about Kastri, should I ask them here? Edited October 19 by John van de Waeter Share this post Link to post
Dave Nottage 557 Posted October 19 9 hours ago, John van de Waeter said: Dave, if I have questions about Kastri, should I ask them here? If they're actual issues, I'd rather them being reported here. I'm sure it's perfectly alright to ask questions in this forum, though. Share this post Link to post
Vanar 2 Posted October 20 On 10/18/2024 at 6:24 PM, Vladsrb said: Hi, i was facing with the same problem... This is my workable solution, in short 🙂 uses System.Net.HttpClient, JOSE.Core.Builder, JOSE.Core.JWT, JOSE.Core.JWA, JOSE.Types.Bytes, JOSE.Types.JSON, JOSE.Signing.RSA, JOSE.Core.JWK JOSE is taken from https://github.com/paolo-rossi/delphi-jose-jwt Info: FGoogleProjectID : is your project ID like 'name-95dc3e3' (console.cloud.google.com) FGoogleServiceEmail: like 'firebase-adminsdk-9tm7h@XXXXXXXX.iam.gserviceaccount.com' FGoogleServicePrivateKey: '-----BEGIN PRIVATE KEY-----...' taken from the JSON file when you create a key for a Google service account (you need to remove the \n characters from the key) function CreateGoogleTokenString: String; begin Result := ''; var JWToken: TJWT := TJWT.Create(TJWTClaims); try try JWToken.Claims.Issuer := FGoogleServiceEmail; JWToken.Claims.SetClaim('scope', 'https://www.googleapis.com/auth/firebase.messaging'); JWToken.Claims.Audience :='https://oauth2.googleapis.com/token'; JWToken.Claims.IssuedAt := Now; JWToken.Claims.Expiration := IncMinute(Now, 60); // not more than 60 var AJWKKey: TJWK := TJWK.Create(FGoogleServicePrivateKey); var LSignature: TJOSEBytes := TJOSE.SerializeCompact(AJWKKey, TJOSEAlgorithmId.RS256, JWToken); Result := LSignature.AsString; except on e: exception do raise Exception.Create('Error creating Google Token String: ' + E.Message); end; finally FreeAndNil(JWToken); end; end; function GetOAuthToken: String; var LResponse: IHTTPResponse; LFormUrlencoded: TStringList; LRequest: THTTPClient; begin Result := ''; LRequest := THTTPClient.Create; LFormUrlEncoded := TStringList.Create; try try var AToken: String := GCCreateGoogleTokenString; LFormUrlEncoded.Add('grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer'); LFormUrlEncoded.Add('assertion=' + AToken); LResponse := LRequest.Post('https://oauth2.googleapis.com/token', LFormUrlencoded); if LResponse.StatusCode = 200 then begin Result := LResponse.ContentAsString(); var JSONObj: TJSONObject := TJSONObject.ParseJSONValue(Result) as TJSONObject; try if Assigned(JSONObj) then Result := JSONObj.GetValue<string>('access_token'); finally JSONObj.Free; end; end; except on e: exception do begin Result := ''; raise Exception.Create('Error getting Google Token: ' + E.Message); end; end; finally LFormUrlencoded.Free; LRequest.Free; end; end; sending a push message with RESTClient: function CreateBodyMessageJSON(AFBToken: String): String; // https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages#Notification begin var AJSONMain: TJSONObject := TJSONObject.Create; try var AJSONMessage: TJSONObject := TJSONObject.Create; AJSONMessage.AddPair('token', AFBToken); // for all var AJSONNotification: TJSONObject := TJSONObject.Create; AJSONNotification.AddPair('body', FBody); AJSONNotification.AddPair('title', FTitle); AJSONMessage.AddPair('notification', AJSONNotification); // for Android var AJSONNotification1: TJSONObject := TJSONObject.Create; AJSONNotification1.AddPair('sound', 'default'); // AJSONNotification1.AddPair('notification_count', TJSONNumber.Create(1)); // if needed var AJSONAndroid: TJSONObject := TJSONObject.Create; AJSONAndroid.AddPair('notification', AJSONNotification1); AJSONMessage.AddPair('android', AJSONAndroid); // for IOS var AJSONAps: TJSONObject := TJSONObject.Create; AJSONAps.AddPair('sound', 'default'); AJSONAps.AddPair('badge', TJSONNumber.Create(1)); // Show badge on app icon on IOS var AJSONPayload: TJSONObject := TJSONObject.Create; AJSONPayload.AddPair('aps', AJSONAps); var AJSONApns: TJSONObject := TJSONObject.Create; AJSONApns.AddPair('payload', AJSONPayload); AJSONMessage.AddPair('apns', AJSONApns); AJSONMain.AddPair('message', AJSONMessage); Result := AJSONMain.ToJSON; finally AJSONMain.Free; end; end; RESTClientPushmessage.BaseURL := 'https://fcm.googleapis.com/v1/projects/' + FGoogleProjectID + '/messages:send'; RESTRequestPusMessage.Params.ParameterByName('Authorization').Value := 'Bearer ' + GCGetOAuthToken; // pkHTTPHEADER RESTRequestPusMessage.Params.ParameterByName('Content-Type').Value := 'application/json' // pkHTTPHEADER RESTRequestPusMessage.Params.ParameterByName('body').Value := CreateBodyMessageJSON(AFireBaseToken); // pkREQUESTBODY RESTRequestPusMessage.Execute; If i miss something let me know... For reading FB token on IOS i use: Firebase SDK for IOS 6.28 (working on Delphi 12.2) Hi, Vladsrb! I tried your solution It works great on Windows! But I need to run it on iOS "-For reading FB token on IOS i use: Firebase SDK for IOS 6.28 (working on Delphi 12.2)" Could you please describe the process in more detail: How do I use Firebase SDK for IOS 6.28, where can I download Firebase SDK (GetIt doesn't work for me for some reason), where and what links should I write in the project? Share this post Link to post
Dave Nottage 557 Posted October 20 3 hours ago, Vanar said: But I need to run it on iOS Why do you need to send push messages from an iOS device? It's a very bad idea to have private key info in an iOS app. In a web server (where messages should be sent from), the executable (and private key) is (or at least should not be) accessible externally. 3 hours ago, Vanar said: How do I use Firebase SDK for IOS 6.28, where can I download Firebase SDK (GetIt doesn't work for me for some reason), From here: https://github.com/firebase/firebase-ios-sdk/releases/download/CocoaPods-6.28.0/Firebase.zip ..but the code example above which sends messages does not require the iOS SDK, which is used for receiving messages. 1 Share this post Link to post
Vanar 2 Posted October 21 11 hours ago, Dave Nottage said: Why do you need to send push messages from an iOS device? It's a very bad idea to have private key info in an iOS app. In a web server (where messages should be sent from), the executable (and private key) is (or at least should not be) accessible externally. I agree, but I just wanted to test it. On iOS, the error is: Share this post Link to post
Dave Nottage 557 Posted October 21 21 minutes ago, Vanar said: On iOS, the error is: Apparently it's because Indy does not have SSL bindings for encryption on iOS. See this issue. Perhaps @Remy Lebeau can comment. 1 Share this post Link to post
Vladsrb 2 Posted October 21 17 hours ago, Vanar said: How do I use Firebase SDK for IOS 6.28 Currently i also get Internal server Error for GetIt, so for FB SDK you can use the link provided by Dave Nottage. For reading FB token from IOS app using FB SDK: Manual Hint: When you add Project/Deployment - GoogleService-Info.plist (downloaded from Firebase for IOS) - check 'Remote path' column, must be: .\ 2 Share this post Link to post