mirror of
https://github.com/bbernhard/signal-cli-rest-api.git
synced 2026-11-04 10:19:51 +00:00
Support account recovery with a recovery key
signal-cli 0.14.9 can recover an existing account with the Recovery Key shown by Signal Android, which is the only way to register an account without a phone number. Add recovery_key, totp and reregister to the register request, for both the CLI and the JSON-RPC mode. Registering by ACI without a recovery key, and verifying by ACI, are rejected up front with a clear error, as there is no phone number to send a verification code to.
This commit is contained in:
parent
6e2d3b94d6
commit
9e3ec82811
@ -55,3 +55,20 @@ func TestAccountIdentifierFromParam(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// An ACI has no phone number to send a code to, so it can only be registered with a recovery key and never verified.
|
||||
func TestRegisterByACIRequiresRecoveryKey(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
aci := gin.Params{{Key: "number", Value: "11111111-1111-4111-8111-111111111111"}, {Key: "token", Value: "123456"}}
|
||||
|
||||
for name, handler := range map[string]gin.HandlerFunc{"register": NewApi(nil).RegisterNumber, "verify": NewApi(nil).VerifyRegisteredNumber} {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Params = aci
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/", strings.NewReader("{}"))
|
||||
handler(c)
|
||||
if w.Code != 400 || !strings.Contains(w.Body.String(), "recovery_key") {
|
||||
t.Fatalf("%s: status = %d, body %s", name, w.Code, w.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,8 +84,11 @@ type Configuration struct {
|
||||
}
|
||||
|
||||
type RegisterNumberRequest struct {
|
||||
UseVoice bool `json:"use_voice,omitempty"`
|
||||
Captcha string `json:"captcha,omitempty"`
|
||||
UseVoice bool `json:"use_voice,omitempty"`
|
||||
Captcha string `json:"captcha,omitempty"`
|
||||
RecoveryKey string `json:"recovery_key,omitempty"` // 64-character Recovery Key shown by Signal Android. Recovers the account without SMS verification.
|
||||
Totp string `json:"totp,omitempty"` // Six-digit TOTP token, only needed if recovery asks for it.
|
||||
Reregister bool `json:"reregister,omitempty"` // Register even if the account is still registered locally.
|
||||
}
|
||||
|
||||
type UnregisterNumberRequest struct {
|
||||
@ -290,7 +293,7 @@ func (a *Api) About(c *gin.Context) {
|
||||
|
||||
// @Summary Register a phone number.
|
||||
// @Tags Devices
|
||||
// @Description Register a phone number with the signal network.
|
||||
// @Description Register a phone number with the signal network. An existing account can instead be recovered with its Recovery Key (recovery_key), in which case no verification is needed. Accounts without a phone number can only be recovered this way, using their ACI (Account Key) as identifier. If the response asks for a TOTP token, repeat the request with totp.
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 201
|
||||
@ -299,7 +302,7 @@ func (a *Api) About(c *gin.Context) {
|
||||
// @Param data body RegisterNumberRequest false "Additional Settings"
|
||||
// @Router /v1/register/{number} [post]
|
||||
func (a *Api) RegisterNumber(c *gin.Context) {
|
||||
_, number, err := accountIdentifierFromParam(c)
|
||||
typ, number, err := accountIdentifierFromParam(c)
|
||||
if err != nil {
|
||||
c.JSON(400, Error{Msg: err.Error()})
|
||||
return
|
||||
@ -326,7 +329,13 @@ func (a *Api) RegisterNumber(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err = a.signalClient.RegisterNumber(number, req.UseVoice, req.Captcha)
|
||||
// without a phone number there is nothing to send a verification code to
|
||||
if typ == utils.IdentifierACI && req.RecoveryKey == "" {
|
||||
c.JSON(400, Error{Msg: "Couldn't process request - registering by ACI requires a recovery_key"})
|
||||
return
|
||||
}
|
||||
|
||||
err = a.signalClient.RegisterNumber(number, req.UseVoice, req.Captcha, req.RecoveryKey, req.Totp, req.Reregister)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *client.InvalidTransportError:
|
||||
@ -430,11 +439,15 @@ func (a *Api) DeleteLocalAccountData(c *gin.Context) {
|
||||
// @Param token path string true "Verification Code"
|
||||
// @Router /v1/register/{number}/verify/{token} [post]
|
||||
func (a *Api) VerifyRegisteredNumber(c *gin.Context) {
|
||||
_, number, err := accountIdentifierFromParam(c)
|
||||
typ, number, err := accountIdentifierFromParam(c)
|
||||
if err != nil {
|
||||
c.JSON(400, Error{Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
if typ == utils.IdentifierACI {
|
||||
c.JSON(400, Error{Msg: "Couldn't process request - only phone numbers can be verified, accounts registered with a recovery_key need no verification"})
|
||||
return
|
||||
}
|
||||
token := c.Param("token")
|
||||
|
||||
pin := ""
|
||||
|
||||
@ -774,16 +774,21 @@ func (s *SignalClient) About() About {
|
||||
return about
|
||||
}
|
||||
|
||||
func (s *SignalClient) RegisterNumber(number string, useVoice bool, captcha string) error {
|
||||
// RegisterNumber registers an account. With a recoveryKey an existing account is recovered instead
|
||||
// (no verification needed), which is the only way to register an account without a phone number.
|
||||
func (s *SignalClient) RegisterNumber(number string, useVoice bool, captcha string, recoveryKey string, totp string, reregister bool) error {
|
||||
var err error
|
||||
var jsonRpc2Client *JsonRpc2Client
|
||||
if s.signalCliMode == JsonRpc {
|
||||
type Request struct {
|
||||
UseVoice bool `json:"voice,omitempty"`
|
||||
Captcha string `json:"captcha,omitempty"`
|
||||
Account string `json:"account,omitempty"`
|
||||
UseVoice bool `json:"voice,omitempty"`
|
||||
Captcha string `json:"captcha,omitempty"`
|
||||
Account string `json:"account,omitempty"`
|
||||
RecoveryKey string `json:"recoveryKey,omitempty"`
|
||||
Totp string `json:"totp,omitempty"`
|
||||
Reregister bool `json:"reregister,omitempty"`
|
||||
}
|
||||
request := Request{Account: number}
|
||||
request := Request{Account: number, RecoveryKey: recoveryKey, Totp: totp, Reregister: reregister}
|
||||
|
||||
if useVoice {
|
||||
request.UseVoice = useVoice
|
||||
@ -809,6 +814,18 @@ func (s *SignalClient) RegisterNumber(number string, useVoice bool, captcha stri
|
||||
command = append(command, []string{"--captcha", captcha}...)
|
||||
}
|
||||
|
||||
if recoveryKey != "" {
|
||||
command = append(command, []string{"--recovery-key", recoveryKey}...)
|
||||
}
|
||||
|
||||
if totp != "" {
|
||||
command = append(command, []string{"--totp", totp}...)
|
||||
}
|
||||
|
||||
if reregister {
|
||||
command = append(command, "--reregister")
|
||||
}
|
||||
|
||||
_, err = s.cliClient.Execute(true, command, "")
|
||||
}
|
||||
|
||||
|
||||
@ -282,6 +282,18 @@ const docTemplate = `{
|
||||
"captcha": {
|
||||
"type": "string"
|
||||
},
|
||||
"recovery_key": {
|
||||
"description": "64-character Recovery Key shown by Signal Android. Recovers the account without SMS verification.",
|
||||
"type": "string"
|
||||
},
|
||||
"reregister": {
|
||||
"description": "Register even if the account is still registered locally.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"totp": {
|
||||
"description": "Six-digit TOTP token, only needed if recovery asks for it.",
|
||||
"type": "string"
|
||||
},
|
||||
"use_voice": {
|
||||
"type": "boolean"
|
||||
}
|
||||
@ -4656,7 +4668,7 @@ const docTemplate = `{
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"description": "Register a phone number with the signal network.",
|
||||
"description": "Register a phone number with the signal network. An existing account can instead be recovered with its Recovery Key (recovery_key), in which case no verification is needed. Accounts without a phone number can only be recovered this way, using their ACI (Account Key) as identifier. If the response asks for a TOTP token, repeat the request with totp.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Account identifier (E.164 phone number or ACI UUID)",
|
||||
|
||||
@ -277,6 +277,18 @@
|
||||
"captcha": {
|
||||
"type": "string"
|
||||
},
|
||||
"recovery_key": {
|
||||
"description": "64-character Recovery Key shown by Signal Android. Recovers the account without SMS verification.",
|
||||
"type": "string"
|
||||
},
|
||||
"reregister": {
|
||||
"description": "Register even if the account is still registered locally.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"totp": {
|
||||
"description": "Six-digit TOTP token, only needed if recovery asks for it.",
|
||||
"type": "string"
|
||||
},
|
||||
"use_voice": {
|
||||
"type": "boolean"
|
||||
}
|
||||
@ -4651,7 +4663,7 @@
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"description": "Register a phone number with the signal network.",
|
||||
"description": "Register a phone number with the signal network. An existing account can instead be recovered with its Recovery Key (recovery_key), in which case no verification is needed. Accounts without a phone number can only be recovered this way, using their ACI (Account Key) as identifier. If the response asks for a TOTP token, repeat the request with totp.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Account identifier (E.164 phone number or ACI UUID)",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user