1 uniffi::setup_scaffolding!();
2
3 use smbcloud_auth_sdk::{
4 apple, client_credentials::ClientCredentials, login, logout, me, oidc, remove, signup,
5 };
6 use smbcloud_model::error_codes::ErrorResponse;
7 use smbcloud_network::environment::Environment;
8
9 // ── Error ────────────────────────────────────────────────────────────────────
10
11 #[derive(Debug, thiserror::Error, uniffi::Error)]
12 pub enum SmbCloudAuthError {
13 #[error("[{error_code}] {message}")]
14 Api { error_code: i32, message: String },
15 }
16
17 impl From<ErrorResponse> for SmbCloudAuthError {
18 fn from(error: ErrorResponse) -> Self {
19 match error {
20 ErrorResponse::Error {
21 error_code,
22 message,
23 } => Self::Api {
24 error_code: error_code as i32,
25 message,
26 },
27 }
28 }
29 }
30
31 // ── Environment ──────────────────────────────────────────────────────────────
32
33 #[derive(uniffi::Enum)]
34 pub enum SmbCloudEnvironment {
35 Dev,
36 Production,
37 }
38
39 impl From<SmbCloudEnvironment> for Environment {
40 fn from(environment: SmbCloudEnvironment) -> Self {
41 match environment {
42 SmbCloudEnvironment::Dev => Self::Dev,
43 SmbCloudEnvironment::Production => Self::Production,
44 }
45 }
46 }
47
48 // ── Account status ───────────────────────────────────────────────────────────
49
50 #[derive(uniffi::Enum)]
51 pub enum SmbCloudAccountStatus {
52 NotFound,
53 Ready { access_token: String },
54 Incomplete { error_code: u32 },
55 }
56
57 impl From<smbcloud_model::login::AccountStatus> for SmbCloudAccountStatus {
58 fn from(status: smbcloud_model::login::AccountStatus) -> Self {
59 match status {
60 smbcloud_model::login::AccountStatus::NotFound => Self::NotFound,
61 smbcloud_model::login::AccountStatus::Ready { access_token } => {
62 Self::Ready { access_token }
63 }
64 smbcloud_model::login::AccountStatus::Incomplete { status } => Self::Incomplete {
65 error_code: status as u32,
66 },
67 }
68 }
69 }
70
71 // ── User ─────────────────────────────────────────────────────────────────────
72
73 #[derive(uniffi::Record)]
74 pub struct SmbCloudUser {
75 pub id: i32,
76 pub email: String,
77 pub created_at: String,
78 pub updated_at: String,
79 }
80
81 impl From<smbcloud_model::account::User> for SmbCloudUser {
82 fn from(user: smbcloud_model::account::User) -> Self {
83 Self {
84 id: user.id,
85 email: user.email,
86 created_at: user.created_at.to_rfc3339(),
87 updated_at: user.updated_at.to_rfc3339(),
88 }
89 }
90 }
91
92 // ── Signup result ────────────────────────────────────────────────────────────
93
94 #[derive(uniffi::Record)]
95 pub struct SmbCloudSignupResult {
96 pub code: Option<i32>,
97 pub message: String,
98 pub user_id: Option<i32>,
99 pub user_email: Option<String>,
100 pub user_created_at: Option<String>,
101 }
102
103 impl From<smbcloud_model::signup::SignupResult> for SmbCloudSignupResult {
104 fn from(result: smbcloud_model::signup::SignupResult) -> Self {
105 Self {
106 code: result.code,
107 message: result.message,
108 user_id: result.data.as_ref().map(|data| data.id),
109 user_email: result.data.as_ref().map(|data| data.email.clone()),
110 user_created_at: result.data.map(|data| data.created_at),
111 }
112 }
113 }
114
115 // ── OIDC types ───────────────────────────────────────────────────────────────
116
117 #[derive(uniffi::Record)]
118 pub struct SmbCloudAuthorizationRequest {
119 pub authorize_url: String,
120 pub redirect_uri: String,
121 pub state: String,
122 pub code_verifier: String,
123 }
124
125 impl From<oidc::AuthorizationRequest> for SmbCloudAuthorizationRequest {
126 fn from(request: oidc::AuthorizationRequest) -> Self {
127 Self {
128 authorize_url: request.authorize_url,
129 redirect_uri: request.redirect_uri,
130 state: request.state,
131 code_verifier: request.code_verifier,
132 }
133 }
134 }
135
136 #[derive(uniffi::Record)]
137 pub struct SmbCloudCallbackPayload {
138 pub code: String,
139 pub state: String,
140 }
141
142 impl From<oidc::CallbackPayload> for SmbCloudCallbackPayload {
143 fn from(payload: oidc::CallbackPayload) -> Self {
144 Self {
145 code: payload.code,
146 state: payload.state,
147 }
148 }
149 }
150
151 #[derive(uniffi::Record)]
152 pub struct SmbCloudTokenResponse {
153 pub access_token: String,
154 pub token_type: String,
155 pub expires_in: Option<i32>,
156 pub refresh_token: Option<String>,
157 pub scope: Option<String>,
158 pub id_token: Option<String>,
159 }
160
161 impl From<oidc::TokenResponse> for SmbCloudTokenResponse {
162 fn from(response: oidc::TokenResponse) -> Self {
163 Self {
164 access_token: response.access_token,
165 token_type: response.token_type,
166 expires_in: response.expires_in,
167 refresh_token: response.refresh_token,
168 scope: response.scope,
169 id_token: response.id_token,
170 }
171 }
172 }
173
174 #[derive(uniffi::Record)]
175 pub struct SmbCloudUserInfo {
176 pub sub: String,
177 pub email: Option<String>,
178 pub email_verified: Option<bool>,
179 pub tenant_id: Option<u64>,
180 pub tenant_slug: Option<String>,
181 }
182
183 impl From<oidc::UserInfo> for SmbCloudUserInfo {
184 fn from(info: oidc::UserInfo) -> Self {
185 Self {
186 sub: info.sub,
187 email: info.email,
188 email_verified: info.email_verified,
189 tenant_id: info.tenant_id,
190 tenant_slug: info.tenant_slug,
191 }
192 }
193 }
194
195 // ── Apple Sign-In types ──────────────────────────────────────────────────────
196
197 #[derive(uniffi::Record)]
198 pub struct SmbCloudAppleAuthorizationRequest {
199 pub authorize_url: String,
200 pub redirect_uri: String,
201 pub state: String,
202 }
203
204 impl From<apple::AppleAuthorizationRequest> for SmbCloudAppleAuthorizationRequest {
205 fn from(request: apple::AppleAuthorizationRequest) -> Self {
206 Self {
207 authorize_url: request.authorize_url,
208 redirect_uri: request.redirect_uri,
209 state: request.state,
210 }
211 }
212 }
213
214 #[derive(uniffi::Record)]
215 pub struct SmbCloudAppleAuthSession {
216 pub access_token: String,
217 pub refresh_token: Option<String>,
218 pub email: Option<String>,
219 pub name: Option<String>,
220 pub provider: String,
221 pub provider_account_id: String,
222 pub state: Option<String>,
223 }
224
225 impl From<apple::AppleAuthSession> for SmbCloudAppleAuthSession {
226 fn from(session: apple::AppleAuthSession) -> Self {
227 Self {
228 access_token: session.access_token,
229 refresh_token: session.refresh_token,
230 email: session.email,
231 name: session.name,
232 provider: session.provider,
233 provider_account_id: session.provider_account_id,
234 state: session.state,
235 }
236 }
237 }
238
239 // ── Main client object ──────────────────────────────────────────────────────
240
241 #[derive(uniffi::Object)]
242 pub struct SmbCloudAuth {
243 environment: Environment,
244 app_id: String,
245 app_secret: String,
246 }
247
248 impl SmbCloudAuth {
249 fn credentials(&self) -> ClientCredentials<'_> {
250 ClientCredentials {
251 app_id: &self.app_id,
252 app_secret: &self.app_secret,
253 }
254 }
255 }
256
257 #[uniffi::export]
258 impl SmbCloudAuth {
259 #[uniffi::constructor]
260 pub fn new(environment: SmbCloudEnvironment, app_id: String, app_secret: String) -> Self {
261 Self {
262 environment: environment.into(),
263 app_id,
264 app_secret,
265 }
266 }
267
268 // ── Email/password auth ──────────────────────────────────────────────
269
270 pub async fn login(
271 &self,
272 email: String,
273 password: String,
274 ) -> Result<SmbCloudAccountStatus, SmbCloudAuthError> {
275 login::login_with_client(self.environment, self.credentials(), email, password)
276 .await
277 .map(Into::into)
278 .map_err(Into::into)
279 }
280
281 pub async fn signup(
282 &self,
283 email: String,
284 password: String,
285 ) -> Result<SmbCloudSignupResult, SmbCloudAuthError> {
286 signup::signup_with_client(self.environment, self.credentials(), email, password)
287 .await
288 .map(Into::into)
289 .map_err(Into::into)
290 }
291
292 pub async fn logout(&self, access_token: String) -> Result<(), SmbCloudAuthError> {
293 logout::logout_with_client(self.environment, self.credentials(), access_token)
294 .await
295 .map_err(Into::into)
296 }
297
298 pub async fn me(&self, access_token: String) -> Result<SmbCloudUser, SmbCloudAuthError> {
299 me::me_with_client(self.environment, self.credentials(), &access_token)
300 .await
301 .map(Into::into)
302 .map_err(Into::into)
303 }
304
305 pub async fn remove_account(&self, access_token: String) -> Result<(), SmbCloudAuthError> {
306 remove::remove_with_client(self.environment, self.credentials(), &access_token)
307 .await
308 .map_err(Into::into)
309 }
310
311 // ── Apple Sign-In ────────────────────────────────────────────────────
312
313 pub fn build_apple_authorization_request(
314 &self,
315 redirect_uri: String,
316 state: Option<String>,
317 ) -> Result<SmbCloudAppleAuthorizationRequest, SmbCloudAuthError> {
318 apple::build_authorization_request_with_client(
319 self.environment,
320 self.credentials(),
321 redirect_uri,
322 state,
323 )
324 .map(Into::into)
325 .map_err(Into::into)
326 }
327
328 pub fn parse_apple_callback_url(
329 &self,
330 callback_url: String,
331 expected_state: Option<String>,
332 ) -> Result<SmbCloudAppleAuthSession, SmbCloudAuthError> {
333 apple::parse_callback_url(&callback_url, expected_state.as_deref())
334 .map(Into::into)
335 .map_err(Into::into)
336 }
337
338 // ── OIDC ─────────────────────────────────────────────────────────────
339
340 pub fn build_oidc_authorization_request(
341 &self,
342 oidc_client_id: String,
343 redirect_uri: String,
344 ) -> Result<SmbCloudAuthorizationRequest, SmbCloudAuthError> {
345 oidc::build_authorization_request(self.environment, &oidc_client_id, redirect_uri)
346 .map(Into::into)
347 .map_err(Into::into)
348 }
349
350 pub fn parse_oidc_callback_url(
351 &self,
352 callback_url: String,
353 ) -> Result<SmbCloudCallbackPayload, SmbCloudAuthError> {
354 oidc::parse_callback_url(&callback_url)
355 .map(Into::into)
356 .map_err(Into::into)
357 }
358
359 pub async fn exchange_oidc_code(
360 &self,
361 oidc_client_id: String,
362 redirect_uri: String,
363 code: String,
364 code_verifier: String,
365 ) -> Result<SmbCloudTokenResponse, SmbCloudAuthError> {
366 oidc::exchange_code(
367 self.environment,
368 &oidc_client_id,
369 &redirect_uri,
370 &code,
371 &code_verifier,
372 )
373 .await
374 .map(Into::into)
375 .map_err(Into::into)
376 }
377
378 pub async fn get_oidc_userinfo(
379 &self,
380 access_token: String,
381 tenant_id: Option<String>,
382 ) -> Result<SmbCloudUserInfo, SmbCloudAuthError> {
383 oidc::get_userinfo(self.environment, &access_token, tenant_id.as_deref())
384 .await
385 .map(Into::into)
386 .map_err(Into::into)
387 }
388 }