Add smbcloud-auth-sdk-apple crate with UniFFI Apple bindings
- Introduce new crate `smbcloud-auth-sdk-apple` for Apple platform bindings - Implement UniFFI scaffolding and FFI-safe types for Auth SDK - Expose async and sync methods for login, signup, logout, OIDC, and Apple Sign-In - Add workspace dependency on `uniffi` - Make `Data` fields in `account.rs` public for FFI access
Seto Elkahfi committed
May 16, 2026 at 16:21 UTC
2cad3eb45be92a7649107ed156f668f3eff4b501
6 files changed
+774
-7
Cargo.lock
+350
-4
@@ -104,6 +104,61 @@ version = "1.0.102"
104
source = "registry+https://github.com/rust-lang/crates.io-index"
105
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
106
107
+[[package]]
108
+name = "askama"
109
+version = "0.13.1"
110
+source = "registry+https://github.com/rust-lang/crates.io-index"
111
+checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7"
112
+dependencies = [
113
+ "askama_derive",
114
+ "itoa",
115
+ "percent-encoding",
116
+ "serde",
117
+ "serde_json",
118
+]
119
+
120
+[[package]]
121
+name = "askama_derive"
122
+version = "0.13.1"
123
+source = "registry+https://github.com/rust-lang/crates.io-index"
124
+checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"
125
+dependencies = [
126
+ "askama_parser",
127
+ "basic-toml",
128
+ "memchr",
129
+ "proc-macro2",
130
+ "quote",
131
+ "rustc-hash",
132
+ "serde",
133
+ "serde_derive",
134
+ "syn 2.0.117",
135
+]
136
+
137
+[[package]]
138
+name = "askama_parser"
139
+version = "0.13.0"
140
+source = "registry+https://github.com/rust-lang/crates.io-index"
141
+checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
142
+dependencies = [
143
+ "memchr",
144
+ "serde",
145
+ "serde_derive",
146
+ "winnow",
147
+]
148
+
149
+[[package]]
150
+name = "async-compat"
151
+version = "0.2.5"
152
+source = "registry+https://github.com/rust-lang/crates.io-index"
153
+checksum = "a1ba85bc55464dcbf728b56d97e119d673f4cf9062be330a9a26f3acf504a590"
154
+dependencies = [
155
+ "futures-core",
156
+ "futures-io",
157
+ "once_cell",
158
+ "pin-project-lite",
159
+ "tokio",
160
+]
161
+
162
[[package]]
163
name = "async-trait"
164
version = "0.1.89"
@@ -144,6 +199,15 @@ version = "0.22.1"
199
source = "registry+https://github.com/rust-lang/crates.io-index"
200
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
201
202
+[[package]]
203
+name = "basic-toml"
204
+version = "0.1.10"
205
+source = "registry+https://github.com/rust-lang/crates.io-index"
206
+checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
207
+dependencies = [
208
+ "serde",
209
+]
210
+
211
[[package]]
212
name = "bitflags"
213
version = "1.3.2"
@@ -198,6 +262,38 @@ version = "1.11.1"
262
source = "registry+https://github.com/rust-lang/crates.io-index"
263
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
264
265
+[[package]]
266
+name = "camino"
267
+version = "1.2.2"
268
+source = "registry+https://github.com/rust-lang/crates.io-index"
269
+checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
270
+dependencies = [
271
+ "serde_core",
272
+]
273
+
274
+[[package]]
275
+name = "cargo-platform"
276
+version = "0.1.9"
277
+source = "registry+https://github.com/rust-lang/crates.io-index"
278
+checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
279
+dependencies = [
280
+ "serde",
281
+]
282
+
283
+[[package]]
284
+name = "cargo_metadata"
285
+version = "0.19.2"
286
+source = "registry+https://github.com/rust-lang/crates.io-index"
287
+checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
288
+dependencies = [
289
+ "camino",
290
+ "cargo-platform",
291
+ "semver",
292
+ "serde",
293
+ "serde_json",
294
+ "thiserror 2.0.18",
295
+]
296
+
297
[[package]]
298
name = "cassowary"
299
version = "0.3.0"
@@ -272,7 +368,7 @@ dependencies = [
368
"atty",
369
"bitflags 1.3.2",
370
"strsim 0.8.0",
275
- "textwrap",
371
+ "textwrap 0.11.0",
372
"unicode-width 0.1.14",
373
"vec_map",
374
]
@@ -672,6 +768,15 @@ dependencies = [
768
"percent-encoding",
769
]
770
771
+[[package]]
772
+name = "fs-err"
773
+version = "2.11.0"
774
+source = "registry+https://github.com/rust-lang/crates.io-index"
775
+checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
776
+dependencies = [
777
+ "autocfg",
778
+]
779
+
780
[[package]]
781
name = "futures-channel"
782
version = "0.3.32"
@@ -688,6 +793,12 @@ version = "0.3.32"
793
source = "registry+https://github.com/rust-lang/crates.io-index"
794
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
795
796
+[[package]]
797
+name = "futures-io"
798
+version = "0.3.32"
799
+source = "registry+https://github.com/rust-lang/crates.io-index"
800
+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
801
+
802
[[package]]
803
name = "futures-sink"
804
version = "0.3.32"
@@ -802,6 +913,23 @@ dependencies = [
913
"url",
914
]
915
916
+[[package]]
917
+name = "glob"
918
+version = "0.3.3"
919
+source = "registry+https://github.com/rust-lang/crates.io-index"
920
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
921
+
922
+[[package]]
923
+name = "goblin"
924
+version = "0.8.2"
925
+source = "registry+https://github.com/rust-lang/crates.io-index"
926
+checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47"
927
+dependencies = [
928
+ "log",
929
+ "plain",
930
+ "scroll",
931
+]
932
+
933
[[package]]
934
name = "hashbrown"
935
version = "0.15.5"
@@ -1420,6 +1548,12 @@ dependencies = [
1548
"autocfg",
1549
]
1550
1551
+[[package]]
1552
+name = "minimal-lexical"
1553
+version = "0.2.1"
1554
+source = "registry+https://github.com/rust-lang/crates.io-index"
1555
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
1556
+
1557
[[package]]
1558
name = "mio"
1559
version = "1.2.0"
@@ -1449,6 +1583,16 @@ dependencies = [
1583
"tempfile",
1584
]
1585
1586
+[[package]]
1587
+name = "nom"
1588
+version = "7.1.3"
1589
+source = "registry+https://github.com/rust-lang/crates.io-index"
1590
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
1591
+dependencies = [
1592
+ "memchr",
1593
+ "minimal-lexical",
1594
+]
1595
+
1596
[[package]]
1597
name = "nu-ansi-term"
1598
version = "0.50.3"
@@ -1654,7 +1798,7 @@ version = "0.13.1"
1798
source = "registry+https://github.com/rust-lang/crates.io-index"
1799
checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
1800
dependencies = [
1657
- "siphasher",
1801
+ "siphasher 1.0.2",
1802
]
1803
1804
[[package]]
@@ -1669,6 +1813,12 @@ version = "0.3.33"
1813
source = "registry+https://github.com/rust-lang/crates.io-index"
1814
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
1815
1816
+[[package]]
1817
+name = "plain"
1818
+version = "0.2.3"
1819
+source = "registry+https://github.com/rust-lang/crates.io-index"
1820
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
1821
+
1822
[[package]]
1823
name = "portable-atomic"
1824
version = "1.13.1"
@@ -2250,6 +2400,26 @@ version = "1.2.0"
2400
source = "registry+https://github.com/rust-lang/crates.io-index"
2401
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
2402
2403
+[[package]]
2404
+name = "scroll"
2405
+version = "0.12.0"
2406
+source = "registry+https://github.com/rust-lang/crates.io-index"
2407
+checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6"
2408
+dependencies = [
2409
+ "scroll_derive",
2410
+]
2411
+
2412
+[[package]]
2413
+name = "scroll_derive"
2414
+version = "0.12.1"
2415
+source = "registry+https://github.com/rust-lang/crates.io-index"
2416
+checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d"
2417
+dependencies = [
2418
+ "proc-macro2",
2419
+ "quote",
2420
+ "syn 2.0.117",
2421
+]
2422
+
2423
[[package]]
2424
name = "security-framework"
2425
version = "3.7.0"
@@ -2278,6 +2448,10 @@ name = "semver"
2448
version = "1.0.28"
2449
source = "registry+https://github.com/rust-lang/crates.io-index"
2450
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
2451
+dependencies = [
2452
+ "serde",
2453
+ "serde_core",
2454
+]
2455
2456
[[package]]
2457
name = "serde"
@@ -2439,6 +2613,12 @@ dependencies = [
2613
"libc",
2614
]
2615
2616
+[[package]]
2617
+name = "siphasher"
2618
+version = "0.3.11"
2619
+source = "registry+https://github.com/rust-lang/crates.io-index"
2620
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
2621
+
2622
[[package]]
2623
name = "siphasher"
2624
version = "1.0.2"
@@ -2457,6 +2637,12 @@ version = "1.15.1"
2637
source = "registry+https://github.com/rust-lang/crates.io-index"
2638
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
2639
2640
+[[package]]
2641
+name = "smawk"
2642
+version = "0.3.2"
2643
+source = "registry+https://github.com/rust-lang/crates.io-index"
2644
+checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
2645
+
2646
[[package]]
2647
name = "smbcloud-auth"
2648
version = "0.4.1"
@@ -2487,6 +2673,17 @@ dependencies = [
2673
"uuid",
2674
]
2675
2676
+[[package]]
2677
+name = "smbcloud-auth-sdk-apple"
2678
+version = "0.4.1"
2679
+dependencies = [
2680
+ "smbcloud-auth-sdk",
2681
+ "smbcloud-model",
2682
+ "smbcloud-network",
2683
+ "thiserror 2.0.18",
2684
+ "uniffi",
2685
+]
2686
+
2687
[[package]]
2688
name = "smbcloud-auth-sdk-py"
2689
version = "0.4.1"
@@ -2550,7 +2747,7 @@ dependencies = [
2747
"tempfile",
2748
"thiserror 2.0.18",
2749
"tokio",
2553
- "toml",
2750
+ "toml 0.8.23",
2751
"tracing",
2752
"tracing-bunyan-formatter",
2753
"tracing-subscriber",
@@ -2668,7 +2865,7 @@ dependencies = [
2865
"serde",
2866
"serde_json",
2867
"smbcloud-model",
2671
- "toml",
2868
+ "toml 0.8.23",
2869
]
2870
2871
[[package]]
@@ -2931,6 +3128,15 @@ dependencies = [
3128
"unicode-width 0.1.14",
3129
]
3130
3131
+[[package]]
3132
+name = "textwrap"
3133
+version = "0.16.2"
3134
+source = "registry+https://github.com/rust-lang/crates.io-index"
3135
+checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
3136
+dependencies = [
3137
+ "smawk",
3138
+]
3139
+
3140
[[package]]
3141
name = "thiserror"
3142
version = "1.0.69"
@@ -3123,6 +3329,15 @@ dependencies = [
3329
"tokio",
3330
]
3331
3332
+[[package]]
3333
+name = "toml"
3334
+version = "0.5.11"
3335
+source = "registry+https://github.com/rust-lang/crates.io-index"
3336
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
3337
+dependencies = [
3338
+ "serde",
3339
+]
3340
+
3341
[[package]]
3342
name = "toml"
3343
version = "0.8.23"
@@ -3396,6 +3611,128 @@ version = "0.2.6"
3611
source = "registry+https://github.com/rust-lang/crates.io-index"
3612
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
3613
3614
+[[package]]
3615
+name = "uniffi"
3616
+version = "0.29.5"
3617
+source = "registry+https://github.com/rust-lang/crates.io-index"
3618
+checksum = "3291800a6b06569f7d3e15bdb6dc235e0f0c8bd3eb07177f430057feb076415f"
3619
+dependencies = [
3620
+ "anyhow",
3621
+ "camino",
3622
+ "cargo_metadata",
3623
+ "clap 4.6.1",
3624
+ "uniffi_bindgen",
3625
+ "uniffi_core",
3626
+ "uniffi_macros",
3627
+ "uniffi_pipeline",
3628
+]
3629
+
3630
+[[package]]
3631
+name = "uniffi_bindgen"
3632
+version = "0.29.5"
3633
+source = "registry+https://github.com/rust-lang/crates.io-index"
3634
+checksum = "a04b99fa7796eaaa7b87976a0dbdd1178dc1ee702ea00aca2642003aef9b669e"
3635
+dependencies = [
3636
+ "anyhow",
3637
+ "askama",
3638
+ "camino",
3639
+ "cargo_metadata",
3640
+ "fs-err",
3641
+ "glob",
3642
+ "goblin",
3643
+ "heck 0.5.0",
3644
+ "indexmap",
3645
+ "once_cell",
3646
+ "serde",
3647
+ "tempfile",
3648
+ "textwrap 0.16.2",
3649
+ "toml 0.5.11",
3650
+ "uniffi_internal_macros",
3651
+ "uniffi_meta",
3652
+ "uniffi_pipeline",
3653
+ "uniffi_udl",
3654
+]
3655
+
3656
+[[package]]
3657
+name = "uniffi_core"
3658
+version = "0.29.5"
3659
+source = "registry+https://github.com/rust-lang/crates.io-index"
3660
+checksum = "f38a9a27529ccff732f8efddb831b65b1e07f7dea3fd4cacd4a35a8c4b253b98"
3661
+dependencies = [
3662
+ "anyhow",
3663
+ "async-compat",
3664
+ "bytes",
3665
+ "once_cell",
3666
+ "static_assertions",
3667
+]
3668
+
3669
+[[package]]
3670
+name = "uniffi_internal_macros"
3671
+version = "0.29.5"
3672
+source = "registry+https://github.com/rust-lang/crates.io-index"
3673
+checksum = "09acd2ce09c777dd65ee97c251d33c8a972afc04873f1e3b21eb3492ade16933"
3674
+dependencies = [
3675
+ "anyhow",
3676
+ "indexmap",
3677
+ "proc-macro2",
3678
+ "quote",
3679
+ "syn 2.0.117",
3680
+]
3681
+
3682
+[[package]]
3683
+name = "uniffi_macros"
3684
+version = "0.29.5"
3685
+source = "registry+https://github.com/rust-lang/crates.io-index"
3686
+checksum = "5596f178c4f7aafa1a501c4e0b96236a96bc2ef92bdb453d83e609dad0040152"
3687
+dependencies = [
3688
+ "camino",
3689
+ "fs-err",
3690
+ "once_cell",
3691
+ "proc-macro2",
3692
+ "quote",
3693
+ "serde",
3694
+ "syn 2.0.117",
3695
+ "toml 0.5.11",
3696
+ "uniffi_meta",
3697
+]
3698
+
3699
+[[package]]
3700
+name = "uniffi_meta"
3701
+version = "0.29.5"
3702
+source = "registry+https://github.com/rust-lang/crates.io-index"
3703
+checksum = "beadc1f460eb2e209263c49c4f5b19e9a02e00a3b2b393f78ad10d766346ecff"
3704
+dependencies = [
3705
+ "anyhow",
3706
+ "siphasher 0.3.11",
3707
+ "uniffi_internal_macros",
3708
+ "uniffi_pipeline",
3709
+]
3710
+
3711
+[[package]]
3712
+name = "uniffi_pipeline"
3713
+version = "0.29.5"
3714
+source = "registry+https://github.com/rust-lang/crates.io-index"
3715
+checksum = "dd76b3ac8a2d964ca9fce7df21c755afb4c77b054a85ad7a029ad179cc5abb8a"
3716
+dependencies = [
3717
+ "anyhow",
3718
+ "heck 0.5.0",
3719
+ "indexmap",
3720
+ "tempfile",
3721
+ "uniffi_internal_macros",
3722
+]
3723
+
3724
+[[package]]
3725
+name = "uniffi_udl"
3726
+version = "0.29.5"
3727
+source = "registry+https://github.com/rust-lang/crates.io-index"
3728
+checksum = "4319cf905911d70d5b97ce0f46f101619a22e9a189c8c46d797a9955e9233716"
3729
+dependencies = [
3730
+ "anyhow",
3731
+ "textwrap 0.16.2",
3732
+ "uniffi_meta",
3733
+ "weedle2",
3734
+]
3735
+
3736
[[package]]
3737
name = "unindent"
3738
version = "0.2.4"
@@ -3652,6 +3989,15 @@ dependencies = [
3989
"rustls-pki-types",
3990
]
3991
3992
+[[package]]
3993
+name = "weedle2"
3994
+version = "5.0.0"
3995
+source = "registry+https://github.com/rust-lang/crates.io-index"
3996
+checksum = "998d2c24ec099a87daf9467808859f9d82b61f1d9c9701251aea037f514eae0e"
3997
+dependencies = [
3998
+ "nom",
3999
+]
4000
+
4001
[[package]]
4002
name = "whoami"
4003
version = "2.1.2"
Cargo.toml
+1
@@ -44,6 +44,7 @@ thiserror = "2.0"
44
tokio = "1.44"
45
tokio-postgres = "0.7"
46
toml = "0.8"
47
+uniffi = "0.29"
48
tracing = "0.1.35"
49
tracing-bunyan-formatter = "0.3.3"
50
tracing-subscriber = "0.3.14"
crates/smbcloud-auth-sdk-apple/Cargo.toml
new
+29
@@ -0,0 +1,29 @@
1
+[package]
2
+name = "smbcloud-auth-sdk-apple"
3
+version = "0.4.1"
4
+edition = "2024"
5
+authors = ["Seto Elkahfi <hej@setoelkahfi.se>"]
6
+description = "Apple platform bindings for the smbCloud Auth SDK."
7
+license = "Apache-2.0"
8
+repository = "https://github.com/smbcloudXYZ/smbcloud-cli"
9
+publish = false
10
+
11
+[lib]
12
+name = "smbcloud_auth"
13
+crate-type = ["lib", "cdylib", "staticlib"]
14
+path = "src/smbcloud_auth_sdk_apple.rs"
15
+
16
+[features]
17
+bindgen = ["uniffi/cli"]
18
+
19
+[[bin]]
20
+name = "uniffi-bindgen"
21
+path = "uniffi-bindgen.rs"
22
+required-features = ["bindgen"]
23
+
24
+[dependencies]
25
+smbcloud-auth-sdk = { workspace = true }
26
+smbcloud-model = { workspace = true }
27
+smbcloud-network = { workspace = true }
28
+thiserror = { workspace = true }
29
+uniffi = { workspace = true, features = ["tokio"] }
crates/smbcloud-auth-sdk-apple/src/smbcloud_auth_sdk_apple.rs
new
+388
@@ -0,0 +1,388 @@
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
+}
crates/smbcloud-auth-sdk-apple/uniffi-bindgen.rs
new
+3
@@ -0,0 +1,3 @@
1
+fn main() {
2
+ uniffi::uniffi_bindgen_main()
3
+}
crates/smbcloud-model/src/account.rs
+3
-3
@@ -32,9 +32,9 @@ pub struct Status {
32
#[derive(Debug, Serialize, Deserialize)]
33
#[tsync]
34
pub struct Data {
35
- id: i32,
36
- email: String,
37
- created_at: String,
35
+ pub id: i32,
36
+ pub email: String,
37
+ pub created_at: String,
38
}
39
40
/// This is smbCloud authorization model