I sent a message to a Signal group chat from Slidge and got an XMPP error back. Conversations says "Send Failure"; I can see it in the Signal app, though it message details there show that 6 people failed to send, 17 were received, 4 were sent but not yet received.
If I send a message to this group from the official signal app the message details show that everyone in the group received it.
In the server logs I see
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: DEBUG:Signal:Received payload:
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "data": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "results": [
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "address": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "uuid": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "networkFailure": false,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unregisteredFailure": true
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "address": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "uuid": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "networkFailure": true,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "proof_required_failure": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "message": "org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException: StatusCode: 428",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "options": [
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "PUSH_CHALLENGE",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "RECAPTCHA"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: ],
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "retry_after": 86400,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "token": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unregisteredFailure": false
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "address": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "uuid": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "networkFailure": true,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "proof_required_failure": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "message": "org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException: StatusCode: 428",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "options": [
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "PUSH_CHALLENGE",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "RECAPTCHA"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: ],
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "retry_after": 86400,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "token": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unregisteredFailure": false
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "address": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "uuid": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "networkFailure": true,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "proof_required_failure": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "message": "org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException: StatusCode: 428",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "options": [
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "PUSH_CHALLENGE",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "RECAPTCHA"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: ],
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "retry_after": 86400,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "token": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unregisteredFailure": false
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "address": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "uuid": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "networkFailure": false,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unregisteredFailure": true
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "address": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "number": "[redacted]",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "uuid": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "networkFailure": true,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "proof_required_failure": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "message": "org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException: StatusCode: 428",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "options": [
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "PUSH_CHALLENGE",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "RECAPTCHA"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: ],
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "retry_after": 86400,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "token": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unregisteredFailure": false
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "address": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "number": "[redacted]",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "uuid": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "networkFailure": false,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "success": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "devices": [
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: 1,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: 2
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: ],
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "duration": 106,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "needsSync": true,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unidentified": true
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unregisteredFailure": false
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "address": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "number": "[redacted]",
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "uuid": "[redacted]"
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "networkFailure": false,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "success": {
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "devices": [
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: 1
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: ],
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "duration": 109,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "needsSync": true,
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unidentified": true
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: "unregisteredFailure": false
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: },
[...]
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: ERROR:slidge.core.gateway.session_dispatcher:Failed to handle incoming stanza: <message id="b352f9c2-8012-464f-a77d-29a977f1b3a0" from="user@example.org/Cheogram.QqZa" type="groupchat" xml:lang="en" to="ir3vkzlwkevuk4tdkn3esmtwiz3xorlzgjevemsmlbwhgtcciffu6sdkoftgkocumrdukpi=@signal.example.org"><body>I don't know about that one Imogen</body><markable xmlns="urn:xmpp:chat-markers:0" /></message>
[...]
Sep 25 14:33:45 xmpp.example.org slidgnal[4910]: NameError: name 'ProofRequiredError' is not defined
The full logs / screenshots are too large to post here but I'll put them up on github and link them.
So there's a couple things here:
Here's full(er) logs and screenshots: https://gist.github.com/kousu/1bb64f22759b361abefad5cba984f630
Thanks for the report.
I see two interesting things in your logs:
- The
NameError
which is a sign that my signald wrapper has an issue building the dataclass instance for this payload.- The fact that you apparently need to solve another captcha, if we believe [https://gitlab.com/signald/signald/-/issues/374](this issue).
I think the best behaviour here would be to have the gateway send you a message: "you need to complete a captcha and paste the redirect URL as a reply here", which is not great at all UX-wise but I don't think I can do better.
In the meantime, if you want to try out if it this would work and don't mind using
signaldctl
, it's probably possible to use it to "solve the challenge".
It seems that the error is at least reproducible: for this particular Signal group, the same recipients are giving me trouble on each Slidge message.
I didn't know that Signal had captchas! I've never run into them before. I guess because I've never done anything too unusual with Signal.
I tried to follow the instructions but failed; do I need to deregister first? If I re-register signald from the command line how do I get slidge hooked back up to the same account?
[root@xmpp ~]# signaldctl account register [redacted] --captcha 'signal-hcaptcha.5fad97ac-7d06-4e44-b18a-b950b20148ff.challenge.P1_[redacted]' 2023/09/26 14:13:16 error registering with server
Here you are trying to register a new account, which has been broken in signald for a while. You are looking for a different "flow" of catpcha here. Possibly, signald does not expose it nicely in its CLI, but you can send "raw json" payloads to the signald daemon.
(I agree it's not great at all UX wise, but if you could test if it solves the issue for you, I can make a nicer wrapper via a slidge command).
Oh I see. But I'm not sure what to put in as the "challenge". I tried using the "token" I got from the error:
Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: "proof_required_failure": { Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: "message": "org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException: StatusCode: 428", Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: "options": [ Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: "PUSH_CHALLENGE", Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: "RECAPTCHA" Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: ], Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: "retry_after": 86400, Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: "token": "687f7311-2330-4058-b835-e153512cf8df" Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: }, Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: "unregisteredFailure": false Sep 27 04:14:15 comms2.kousu.ca slidgnal[4910]: },
but it failed:
[root@xmpp ~]# signaldctl raw v1 submit_challenge '{"account": "[redacted]", "captcha_token": "signal-hcaptcha.5fad97ac-7d06-4e44-b18a-b950b20148ff.registration.P1_eyJ0[redacted]", "challenge": "687f7311-2330-4058-b835-e153512cf8df"}' Error: signald error: {"exceptions":["org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException"],"message":"org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException: [400] Bad response: 400 "} Usage: signaldctl raw version type [json] [flags] Flags: -h, --help help for raw Global Flags: --config string config file (default "/root/.config/signaldctl.yaml") -o, --output-format string the output format. options are usually table, yaml and json, default is usually table. Some commands have other options. (default "default") --socket string the path to the signald socket file signald error: {"exceptions":["org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException"],"message":"org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException: [400] Bad response: 400 "}
then I tried using the UUID that was embedded in the output from https://signalcaptchas.org/registration/generate -- and removing it from
captcha_token
-- but it didn't work either[root@xmpp ~]# signaldctl raw v1 submit_challenge '{"account": "[redacted]", "captcha_token": "eyJ0[redacted]", "challenge": "5fad97ac-7d06-4e44-b18a-b950b20148ff"}' Error: signald error: {"exceptions":["org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException"],"message":"org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException: [400] Bad response: 400 "} Usage: signaldctl raw version type [json] [flags] Flags: -h, --help help for raw Global Flags: --config string config file (default "/root/.config/signaldctl.yaml") -o, --output-format string the output format. options are usually table, yaml and json, default is usually table. Some commands have other options. (default "default") --socket string the path to the signald socket file signald error: {"exceptions":["org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException"],"message":"org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException: [400] Bad response: 400 "}
https://signalcaptchas.org/registration/generate seems like the wrong link to use: that's for registration, and puts the word "registration" in the output. I'm not trying to register, I'm trying to answer a challenge.
And do you think I need to answer a separate challenge for each recipient that rejected me? In the original logs you can see the 428 responses are associated with individual recipients, and each of them says
"options": [ "PUSH_CHALLENGE", "RECAPTCHA" ]
, as if I need to do one challenge for each.
Try with this URL: https://signalcaptchas.org/challenge/generate to generate the captcha
Using the token from the error message sounds good, maybe it expires at some point though?
Disclaimer: I am not sure of anything I wrote :-)
Well, it accepts captchas generated with that link!
[root@xmpp ~]# signaldctl raw v1 submit_challenge '{"account": "[redacted]", "challenge": "c27f5d96-b4cb-4ed2-91a9-9434f4135230", "captcha_token": "signal-hcaptcha.5fad97ac-7d06-4e44-b18a-b950b20148ff.challenge.P1_eyJ0e[redacted]"}' {}[root@xmpp ~]# echo $? 0
And resubmitting the same captcha gives an error:
[root@xmpp ~]# signaldctl raw v1 submit_challenge '{"account": "[redacted]", "challenge": "c27f5d96-b4cb-4ed2-91a9-9434f4135230", "captcha_token": "signal-hcaptcha.5fad97ac-7d06-4e44-b18a-b950b20148ff.challenge.P1_eyJ0e[redacted]"}' Error: signald error: {"exceptions":["org.whispersystems.signalservice.api.push.exceptions.MalformedResponseException","com.fasterxml.jackson.databind.exc.MismatchedInputException"],"message":"org.whispersystems.signalservice.api.push.exceptions.MalformedResponseException: Unable to parse entity"} Usage: signaldctl raw version type [json] [flags] Flags: -h, --help help for raw Global Flags: --config string config file (default "/root/.config/signaldctl.yaml") -o, --output-format string the output format. options are usually table, yaml and json, default is usually table. Some commands have other options. (default "default") --socket string the path to the signald socket file signald error: {"exceptions":["org.whispersystems.signalservice.api.push.exceptions.MalformedResponseException","com.fasterxml.jackson.databind.exc.MismatchedInputException"],"message":"org.whispersystems.signalservice.api.push.exceptions.MalformedResponseException: Unable to parse entity"}
but nothing I submit makes a difference to. In fact it accepts anything from
uuidgen
, or not even using a UUID at all aschallenge
.[root@xmpp ~]# signaldctl raw v1 submit_challenge '{"account": "[redacted]", "challenge": "signalisconfusing", "captcha_token": "signal-hcaptcha.5fad97ac-7d06-4e44-b18a-b950b20148ff.challenge.P1_eyJ0e[redacted]"}' {}[root@xmpp ~]# echo $? 0
This seems hard! We'll probably have to go diving through https://github.com/signalapp/Signal-Server to figure it out.
It seems like my original instincts were in the right direction; I found this:
public class AnswerRecaptchaChallengeRequest extends AnswerChallengeRequest { @Schema(description = "The value of the token field from the server's 428 response") @NotBlank private String token; @Schema( description = "A string representing a solved captcha", example = "signal-hcaptcha.30b01b46-d8c9-4c30-bbd7-9719acfe0c10.challenge.abcdefg1345") @NotBlank private String captcha;
So yes, the token returned in the ProofRequiredError is somehow involved. That data structure is named "recaptcha" but the example says "hcaptcha" so I think that's the right code, it's probably just bitrotted a bit.
Over in this seems to be the handler that is responding to me which indeed uses
AnswerRecaptchaChallengeRequest
:ChallengeController.java
@Path("/v1/challenge") @Tag(name = "Challenge") public class ChallengeController { private final RateLimitChallengeManager rateLimitChallengeManager; private static final String CHALLENGE_RESPONSE_COUNTER_NAME = name(ChallengeController.class, "challengeResponse"); private static final String CHALLENGE_TYPE_TAG = "type"; public ChallengeController(final RateLimitChallengeManager rateLimitChallengeManager) { this.rateLimitChallengeManager = rateLimitChallengeManager; } @PUT @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Operation( summary = "Submit proof of a challenge completion", description = """ Some server endpoints (the "send message" endpoint, for example) may return a 428 response indicating the client must complete a challenge before continuing. Clients may use this endpoint to provide proof of a completed challenge. If successful, the client may then continue their original operation. """, requestBody = @RequestBody(content = {@Content(schema = @Schema(oneOf = {AnswerPushChallengeRequest.class, AnswerRecaptchaChallengeRequest.class}))}) ) @ApiResponse(responseCode = "200", description = "Indicates the challenge proof was accepted") @ApiResponse(responseCode = "413", description = "Too many attempts", headers = @Header( name = "Retry-After", description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed")) @ApiResponse(responseCode = "429", description = "Too many attempts", headers = @Header( name = "Retry-After", description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed")) public Response handleChallengeResponse(@Auth final AuthenticatedAccount auth, @Valid final AnswerChallengeRequest answerRequest, @HeaderParam(HttpHeaders.X_FORWARDED_FOR) final String forwardedFor, @HeaderParam(HttpHeaders.USER_AGENT) final String userAgent) throws RateLimitExceededException, IOException { Tags tags = Tags.of(UserAgentTagUtil.getPlatformTag(userAgent)); try { if (answerRequest instanceof final AnswerPushChallengeRequest pushChallengeRequest) { tags = tags.and(CHALLENGE_TYPE_TAG, "push"); rateLimitChallengeManager.answerPushChallenge(auth.getAccount(), pushChallengeRequest.getChallenge()); } else if (answerRequest instanceof AnswerRecaptchaChallengeRequest recaptchaChallengeRequest) { tags = tags.and(CHALLENGE_TYPE_TAG, "recaptcha"); final String mostRecentProxy = HeaderUtils.getMostRecentProxy(forwardedFor).orElseThrow(() -> new BadRequestException()); boolean success = rateLimitChallengeManager.answerRecaptchaChallenge( auth.getAccount(), recaptchaChallengeRequest.getCaptcha(), mostRecentProxy, userAgent); if (!success) { return Response.status(428).build(); } } else { tags = tags.and(CHALLENGE_TYPE_TAG, "unrecognized"); } } finally { Metrics.counter(CHALLENGE_RESPONSE_COUNTER_NAME, tags).increment(); } return Response.status(200).build(); } @POST @Path("/push") @Operation( summary = "Request a push challenge", description = """ Clients may proactively request a push challenge by making an empty POST request. Push challenges will only be sent to the requesting account’s main device. When the push is received it may be provided as proof of completed challenge to /v1/challenge. APNs challenge payloads will be formatted as follows: ``` { "aps": { "sound": "default", "alert": { "loc-key": "APN_Message" } }, "rateLimitChallenge": "{CHALLENGE_TOKEN}" } ``` FCM challenge payloads will be formatted as follows: ``` {"rateLimitChallenge": "{CHALLENGE_TOKEN}"} ``` Clients may retry the PUT in the event of an HTTP/5xx response (except HTTP/508) from the server, but must implement an exponential back-off system and limit the total number of retries. """ ) @ApiResponse(responseCode = "200", description = """ Indicates a payload to the account's primary device has been attempted. When clients receive a challenge push notification, they may issue a PUT request to /v1/challenge. """) @ApiResponse(responseCode = "404", description = """ The server does not have a push notification token for the authenticated account’s main device; clients may add a push token and try again """) @ApiResponse(responseCode = "413", description = "Too many attempts", headers = @Header( name = "Retry-After", description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed")) @ApiResponse(responseCode = "429", description = "Too many attempts", headers = @Header( name = "Retry-After", description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed")) public Response requestPushChallenge(@Auth final AuthenticatedAccount auth) { try { rateLimitChallengeManager.sendPushChallenge(auth.getAccount()); return Response.status(200).build(); } catch (final NotPushRegisteredException e) { return Response.status(404).build(); } } }
I am looking at this code, and comparing it to signald, and wondering if maybe the fields aren't named right? Signal-Server has "token" and "captcha", and Signald has "challenge" and "captcha_token".
I tried using the names Signal-Server uses but signald(?) rejected me:
[root@comms2 ~]# signaldctl raw v1 submit_challenge '{"account": "[redacted]", "token": "1d82892d-352a-4424-82c2-14cc6c9f88b0", "captcha": "signal-hcaptcha.1d82892d-352a-4424-82c2-14cc6c9f88b0.challenge.P1_eyJ0e[redacted]"}' Error: signald error: {"validationResults":["missing required argument: challenge"],"message":"input validation failed, please check the request and try again."}
I discovered
challenge
does seem to be in thesignal-hcaptcha.....
format, because if I submit a correctly captcha response from https://signalcaptchas.org/challenge/generate -- without thesignalcaptcha://
-- then it returns{}
, but if I mangle a single character or prependsignalcaptcha://
then it errors.I also discovered
captcha_token
isn't necessary -- at least, leaving it off doesn't provoke an error:[root@xmpp ~]# signaldctl raw v1 submit_challenge '{"account": "[redacted]", "challenge": "signal-hcaptcha.5fad97ac-7d06-4e44-b18a-b950b20148ff.challenge.P1_eyJ0[redacted]"}' {}[root@xmpp ~]#
But so far I haven't figured out how to actually get unblocked.
Maybe I need to submit the same
challenge
(i.e. captcha response) for each of thetokens
for the recipients that are blocking me, before Signal-Server will unblock me to each of them?
I discovered challenge does seem to be in the signal-hcaptcha..... format,
hang on, this contradicts what I just said above, that
challenge
can be in any format and it'scaptcha_token
that should have the signed captcha response in it.I've got myself rate limited so I can't test again to be sure which way it is right now.
Any update on this? Did you manage to get unblocked? I'd happily provide an (XMPP) interface to give the challenge result, but I'm not even sure how this stuff works :-/
Slidge is moving away from sourcehut. For updates on this issue, visit codeberg.
nicoco referenced this ticket in commit 3e0cff2.