TM-SGNL-iOS/SignalServiceKit/Network/API/Requests/Registration/RegistrationServiceResponses.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

355 lines
16 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public import LibSignalClient
public enum RegistrationServiceResponses {
// MARK: - Registration Session Endpoints
public enum BeginSessionResponseCodes: Int, UnknownEnumCodable {
/// Success. Response body has `RegistrationSession` object.
case success = 200
case missingArgument = 400
case invalidArgument = 422
/// The caller is not permitted to create a verification session and must wait before trying again.
/// Response will include a 'retry-after' header.
case retry = 429
case unexpectedError = -1
static public var unknown: Self { .unexpectedError }
}
public enum FetchSessionResponseCodes: Int, UnknownEnumCodable {
/// Success. Response body has `RegistrationSession` object.
case success = 200
/// No session was found with the given ID. A new session should be initiated.
case missingSession = 404
case unexpectedError = -1
static public var unknown: Self { .unexpectedError }
}
public enum FulfillChallengeResponseCodes: Int, UnknownEnumCodable {
/// Success. Response body has `RegistrationSession` object.
case success = 200
/// E.g. the challenge token provided did not match.
/// Response body has `RegistrationSession` object.
case notAccepted = 403
/// No session was found with the given ID. A new session should be initiated.
case missingSession = 404
case malformedRequest = 422
case unexpectedError = -1
static public var unknown: Self { .unexpectedError }
}
public enum RequestVerificationCodeResponseCodes: Int, UnknownEnumCodable {
/// Success. Response body has `RegistrationSession` object.
case success = 200
case malformedRequest = 400
/// No session was found with the given ID. A new session should be initiated.
case missingSession = 404
/// The current session state disallows requesting a code.
/// The client may have to fulfill some challenge before proceeding,
/// or the session might already be verified. Check the session object to know.
/// Response body has `RegistrationSession` object.
case disallowed = 409
/// The chosen transport mode is not supported (likely scoped to the e164),
/// but another transport may be supported.
case transportError = 418
/// May need to wait before trying again; check session object for timeouts.
/// If no timeout is specified, a different transport or starting a fresh session may be required.
/// Response body has `RegistrationSession` object.
case retry = 429
/// The attempt to send a verification code failed because an external service (e.g. the SMS provider) refused to deliver the code.
/// Response body has `SendVerificationCodeFailedResponse` with more detailed information.
case providerFailure = 440
case unexpectedError = -1
static public var unknown: Self { .unexpectedError }
}
public enum SubmitVerificationCodeResponseCodes: Int, UnknownEnumCodable {
/// The code was valid, but may not be correct. The
/// `isVerified` field on the session object indicates
/// correctness.
/// Response body has `RegistrationSession` object.
case success = 200
/// The code was illegally formatted.
case malformedRequest = 400
/// No session was found with the given ID. A new session should be initiated.
case missingSession = 404
/// This session will not accept additional verification code submissions either because no code has been sent for this session
/// (clients must request and presumably receive a verification code before submitting a code)
/// or because the phone number has already been verified with another code.
/// Response body has `RegistrationSession` object.
case newCodeRequired = 409
/// May need to wait before trying again; check session object for timeouts.
/// If no timeout is specified, sending a new code or starting a fresh session may be required.
/// Response body has `RegistrationSession` object.
case retry = 429
case unexpectedError = -1
static public var unknown: Self { .unexpectedError }
}
public struct RegistrationSession: Codable {
/// An opaque identifier for this session.
/// Clients will need to provide this ID to the API for subsequent operations.
/// The identifier will be made of URL-safe characters and will be less than 1024 bytes in length.
public let id: String
/// The time at which a client will next be able to request a verification SMS for this session.
/// If null, no further requests to send a verification SMS will be accepted.
/// Units are seconds from current time given in `X-Signal-Timestamp` header.
public let nextSms: Int?
/// The time at which a client will next be able to request a verification phone call for this session.
/// If null, no further requests to make a verification phone call will be accepted.
/// Units are seconds from current time given in `X-Signal-Timestamp` header.
public let nextCall: Int?
/// The time at which a client will next be able to submit a verification code for this session.
/// If null, no further attempts to submit a verification code will be accepted in the scope of this session.
/// Units are seconds from current time given in `X-Signal-Timestamp` header.
public let nextVerificationAttempt: Int?
/// Indicates whether clients are allowed to request verification code delivery via any transport mechanism.
/// If false, clients should provide the information listed in the `requestedInformation` list until
/// this field is `true` or the list of requested information contains no more options the client can fulfill.
/// If true, clients must still abide by the time limits set in `nextSms`, `nextCall`, and so on.
public let allowedToRequestCode: Bool
/// A list of additional information a client may be required to provide before requesting verification code delivery.
/// Additional requirements may appear in the future, and clients must be prepared to handle these cases gracefully
/// (e.g. by prompting users to update their copy of the app if unrecognized values appear in this list).
public let requestedInformation: [Challenge]
/// Indicates whether the caller has submitted a correct verification code for this session.
public let verified: Bool
public enum Challenge: String, UnknownEnumCodable {
case unknown
case captcha
case pushChallenge
}
}
public struct SendVerificationCodeFailedResponse: Codable {
/// Indicates whether the failure was permanent.
/// If true, clients should not retry the request without modification
/// (practically, this most likely means clients will need to ask users to re-enter their phone number).
/// If false, clients may retry the request after a reasonable delay.
public let permanentFailure: Bool
/// An identifier that indicates the cause of the failure.
/// This identifier is provided on a best-effort basis; it may or may not be present, and may include
/// values not recognized by the current version of the client.
/// Clients should be prepared to handle missing or unrecognized values.
public let reason: Reason?
public enum Reason: String, UnknownEnumCodable {
case unknown
/// The provider understood the request, but declined to deliver a verification SMS/call.
/// (potentially due to fraud prevention rules)
case providerRejected
/// The provider could not be reached or did not respond to the request to send a verification code in a timely manner
case providerUnavailable
/// Some part of the request was not understood or accepted by the provider.
/// (e.g. the provider did not recognize the phone number as a valid number for the selected transport)
case illegalArgument
}
}
// MARK: - SVR2 Auth Check
public enum SVR2AuthCheckResponseCodes: Int, UnknownEnumCodable {
/// Success. Response body has `SVR2AuthCheckResponse` object.
case success = 200
/// The server couldn't parse the set of credentials.
case malformedRequest = 422
/// The POST request body is not valid JSON.
case invalidJSON = 400
case unexpectedError = -1
static public var unknown: Self { .unexpectedError }
}
public struct SVR2AuthCheckResponse: Codable {
public let matches: [String: Result]
public enum Result: String, UnknownEnumCodable {
/// At most one credential will be marked as a `match` per request.
/// Clients should use this credential when re-registering the associated phone number.
case match
/// The provided credential is valid and should be retained by the client,
/// but cannot be used to re-register the provided number.
case notMatch = "not-match"
/// Indicates that the credential may not be used to re-register any phone number and should be discarded.
case invalid
// Server API explicitly says clients should treat unrecognized values as invalid.
static public var unknown: Self { return .invalid }
}
public func result(for credential: SVR2AuthCredential) -> Result? {
let key = "\(credential.credential.username):\(credential.credential.password)"
return matches[key]
}
}
// MARK: - Account Creation/Change Number
public enum AccountCreationResponseCodes: Int, UnknownEnumCodable {
/// Success. Response body has `AccountIdentityResponse`.
case success = 200
/// Incorrect request body shape, missing required Authorization header,
/// or Authorization e164 did not match e164 from session.
/// Response body has a string error message.
case malformedRequest = 400
/// The Authorization header was invalid or the provided credentials were insufficient
/// to verify ownership of the given phone number.
/// Response body has an optional string error message.
case unauthorized = 401
/// The provided registration recovery password is either incorrect
/// or registration via reg recovery password is impossible for this number.
case regRecoveryPasswordRejected = 403
/// The caller has not explicitly elected to skip transferring data
/// from another device, but a device transfer is technically possible.
case deviceTransferPossible = 409
/// Response body has an optional string error message.
///
/// NOTE: if `requireAtomic` is set on the request but other
/// atomic account creation fields are nil, the server will return 422.
case invalidArgument = 422
/// An account with the given phone number already exists and has a registration lock,
/// and the client has not provided appropriate reglock credentials (either because the
/// user inputted the wrong PIN, or because the client has the wrong random number
/// used to generate the master key).
/// Response body has `RegistrationLockFailureResponse`.
case reglockFailed = 423
/// The caller is not permitted to create an account and must wait before trying again.
/// Response will include a 'retry-after' header.
case retry = 429
case unexpectedError = -1
static public var unknown: Self { .unexpectedError }
}
public enum ChangeNumberResponseCodes: Int, UnknownEnumCodable {
/// Success. Response body has `AccountIdentityResponse`.
case success = 200
/// Incorrect request body shape, missing required Authorization header,
/// or Authorization e164 did not match e164 from session.
/// Response body has a string error message.
case malformedRequest = 400
/// The provided credentials were insufficient to verify ownership of the given phone number.
case unauthorized = 401
/// The provided registration recovery password is either incorrect
/// or registration via reg recovery password is impossible for this number.
case regRecoveryPasswordRejected = 403
/// The devices to notify in the request did not match the known
/// linked devices.
case mismatchedDevicesToNotify = 409
/// The devices to notify in the request were correct, but their
/// provided registrationIds did not match.
case mismatchedDevicesToNotifyRegistrationIds = 410
/// Response body has an optional string error message.
case invalidArgument = 422
/// An account with the given phone number already exists and has a registration lock,
/// and the client has not provided appropriate reglock credentials (either because the
/// user inputted the wrong PIN, or because the client has the wrong random number
/// used to generate the master key).
/// Response body has `RegistrationLockFailureResponse`.
case reglockFailed = 423
/// The caller is not permitted to change the number and must wait before trying again.
/// Response will include a 'retry-after' header.
case retry = 429
case unexpectedError = -1
static public var unknown: Self { .unexpectedError }
}
public struct AccountIdentityResponse: Codable, Equatable {
/// The users account identifier.
@AciUuid public var aci: Aci
/// The user's phone number identifier.
@PniUuid public var pni: Pni
/// The phone number associated with the PNI.
public let e164: E164
/// The username associated with the ACI.
public let username: String?
/// Whether the account has any data in SVR.
public let hasPreviouslyUsedSVR: Bool
public init(aci: Aci, pni: Pni, e164: E164, username: String?, hasPreviouslyUsedSVR: Bool) {
self._aci = aci.codableUuid
self._pni = pni.codableUuid
self.e164 = e164
self.username = username
self.hasPreviouslyUsedSVR = hasPreviouslyUsedSVR
}
public enum CodingKeys: String, CodingKey {
case aci = "uuid"
case pni
case e164 = "number"
case username
case hasPreviouslyUsedSVR = "storageCapable"
}
}
public struct RegistrationLockFailureResponse: Decodable {
/// Time remaining until the registration lock expires and the account
/// can be taken over.
public let timeRemainingMs: Int
/// A credential with which the client can talk to SVR2 server to
/// recover the SVR master key, and from it the reglock token,
/// using the user's PIN.
public let svr2AuthCredential: SVR2AuthCredential
public enum CodingKeys: String, CodingKey {
case timeRemainingMs = "timeRemaining"
case svr2AuthCredential = "svr2Credentials"
}
public init(
timeRemainingMs: Int,
svr2AuthCredential: SVR2AuthCredential
) {
self.timeRemainingMs = timeRemainingMs
self.svr2AuthCredential = svr2AuthCredential
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
timeRemainingMs = try container.decode(Int.self, forKey: .timeRemainingMs)
let svr2Credential = try container.decode(RemoteAttestation.Auth.self, forKey: .svr2AuthCredential)
self.svr2AuthCredential = .init(credential: svr2Credential)
}
}
// MARK: Check Proxy Connection
public enum CheckProxyConnectionResponseCodes: Int, UnknownEnumCodable {
case connected = 400
case failure = -1
static public var unknown: Self { .failure }
public init(rawValue: RawValue) {
switch rawValue {
case 200..<300:
self = .connected
case 400..<500:
self = .connected
default:
self = .failure
}
}
}
}