1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use crate::core::{IntoBody, Json, Result};
platform_service! {
/// Autenticação e conta (`/auth/*`, `/profile*`, `/password/*`).
///
/// [`login`](AuthService::login), [`verify_2fa`](AuthService::verify_2fa)
/// e [`refresh`](AuthService::refresh) guardam automaticamente o Bearer
/// Token retornado no cliente, deixando as próximas chamadas
/// autenticadas.
///
/// ```no_run
/// # use apibrasil::ApiBrasil;
/// # use serde_json::json;
/// # async fn exemplo(api: ApiBrasil) -> apibrasil::Result<()> {
/// let sessao = api.auth().login(json!({
/// "email": "voce@empresa.com.br",
/// "password": "******",
/// })).await?;
///
/// if apibrasil::requires_2fa(&sessao) {
/// let challenge = sessao["challenge"].clone();
/// api.auth().send_2fa(json!({ "challenge": challenge, "method": "email" })).await?;
/// api.auth().verify_2fa(json!({ "challenge": challenge, "code": "000000" })).await?;
/// }
/// # Ok(())
/// # }
/// ```
AuthService
}
impl AuthService {
/// Autentica com email/senha: `POST /auth/login`.
///
/// `body` aceita `email`, `password` e `turnstile_token`. Se a conta
/// tiver 2FA, a resposta traz `requires_2fa` e `challenge` — use
/// [`send_2fa`](Self::send_2fa) + [`verify_2fa`](Self::verify_2fa)
/// para concluir.
pub async fn login(&self, body: impl IntoBody) -> Result<Json> {
let response = self.base.post("auth/login", body).await?;
self.store_token(&response);
Ok(response)
}
/// Conclui o login com o código 2FA:
/// `POST /auth/login/verify-2fa`.
pub async fn verify_2fa(&self, body: impl IntoBody) -> Result<Json> {
let response = self.base.post("auth/login/verify-2fa", body).await?;
self.store_token(&response);
Ok(response)
}
/// Renova o JWT: `POST /refresh`.
pub async fn refresh(&self) -> Result<Json> {
let response = self.base.post("refresh", None).await?;
self.store_token(&response);
Ok(response)
}
/// Encerra a sessão e limpa o Bearer Token do cliente:
/// `POST /auth/logout`.
pub async fn logout(&self) -> Result<Json> {
let response = self.base.post("auth/logout", None).await;
self.base.http().set_bearer_token(None);
response
}
/// Guarda no cliente o token devolvido pela plataforma
/// (`authorization.token` ou `token`, conforme a rota).
fn store_token(&self, response: &Json) {
if let Some(token) = extract_token(response) {
self.base.http().set_bearer_token(Some(token));
}
}
}
json_body! {
AuthService {
/// Envia o código 2FA pelo método escolhido (`email`, `sms`,
/// `whatsapp`, `call`): `POST /auth/2fa/send`.
send_2fa => post "auth/2fa/send",
/// Cria uma conta: `POST /auth/register`.
register => post "auth/register",
/// Cadastro simplificado: `POST /auth/register/simple`.
register_simple => post "auth/register/simple",
/// Dispara a verificação de email/celular:
/// `POST /auth/verification/send`.
verification_send => post "auth/verification/send",
/// Confirma o código de verificação:
/// `POST /auth/verification/verify`.
verification_verify => post "auth/verification/verify",
/// Inicia a recuperação de senha: `POST /auth/password/forgot`.
password_forgot => post "auth/password/forgot",
/// Valida o código de recuperação:
/// `POST /auth/password/verify-code`.
password_verify_code => post "auth/password/verify-code",
/// Redefine a senha: `POST /auth/password/reset`.
password_reset => post "auth/password/reset",
/// Reenvia o código de recuperação:
/// `POST /auth/password/resend`.
password_resend => post "auth/password/resend",
/// Troca a senha com a sessão ativa: `POST /password/change`.
change_password => post "password/change",
/// Atualiza o perfil: `PUT /profile/me`.
update_me => put "profile/me",
}
}
json_get! {
AuthService {
/// Lista os métodos 2FA ativos da conta: `GET /auth/2fa/methods`.
two_factor_methods => "auth/2fa/methods",
/// Perfil atual: `GET /profile/me`.
me => "profile/me",
/// Valida o token atual: `GET /auth/verify`.
verify => "auth/verify",
}
}
json_empty! {
AuthService {
/// Perfil completo, com estatísticas: `POST /profile`.
profile => post "profile",
/// Rotaciona o token: `POST /auth/token/rotate`.
token_rotate => post "auth/token/rotate",
/// Revoga o token atual: `POST /auth/token/revoke`.
token_revoke => post "auth/token/revoke",
}
}
/// Lê o Bearer Token de uma resposta de autenticação
/// (`authorization.token` ou `token`).
pub fn extract_token(response: &Json) -> Option<String> {
if let Some(token) = response
.get("authorization")
.and_then(|authorization| authorization.get("token"))
.and_then(Json::as_str)
{
if !token.is_empty() {
return Some(token.to_string());
}
}
let token = response.get("token")?.as_str()?;
if token.is_empty() {
None
} else {
Some(token.to_string())
}
}
/// Informa se a resposta de login exige segundo fator.
pub fn requires_2fa(response: &Json) -> bool {
response.get("requires_2fa") == Some(&Json::Bool(true))
}