Page MenuHomePhabricator

D8094.id27630.diff
No OneTemporary

D8094.id27630.diff

diff --git a/services/commtest/Cargo.lock b/services/commtest/Cargo.lock
--- a/services/commtest/Cargo.lock
+++ b/services/commtest/Cargo.lock
@@ -8,6 +8,17 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
+[[package]]
+name = "argon2"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
+dependencies = [
+ "base64ct",
+ "blake2",
+ "password-hash",
+]
+
[[package]]
name = "async-stream"
version = "0.3.3"
@@ -92,6 +103,12 @@
"tower-service",
]
+[[package]]
+name = "base16ct"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
+
[[package]]
name = "base64"
version = "0.13.1"
@@ -104,12 +121,33 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
+[[package]]
+name = "base64"
+version = "0.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+[[package]]
+name = "blake2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
+dependencies = [
+ "digest 0.10.7",
+]
+
[[package]]
name = "block-buffer"
version = "0.10.3"
@@ -119,6 +157,12 @@
"generic-array",
]
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
[[package]]
name = "byteorder"
version = "1.4.3"
@@ -149,6 +193,18 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "comm-opaque2"
+version = "0.2.0"
+dependencies = [
+ "argon2",
+ "log",
+ "opaque-ke",
+ "rand",
+ "tonic 0.9.2",
+ "wasm-bindgen",
+]
+
[[package]]
name = "commtest"
version = "0.1.0"
@@ -157,6 +213,7 @@
"async-stream",
"base64 0.20.0",
"bytesize",
+ "comm-opaque2",
"derive_more",
"futures",
"futures-util",
@@ -165,16 +222,23 @@
"num_cpus",
"openssl",
"prost",
+ "rand",
"serde_json",
"sha2",
"tokio",
"tokio-tungstenite",
- "tonic",
+ "tonic 0.8.3",
"tonic-build",
"tunnelbroker_messages",
"url",
]
+[[package]]
+name = "const-oid"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
+
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -190,6 +254,18 @@
"libc",
]
+[[package]]
+name = "crypto-bigint"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
+dependencies = [
+ "generic-array",
+ "rand_core",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "crypto-common"
version = "0.1.6"
@@ -200,6 +276,39 @@
"typenum",
]
+[[package]]
+name = "curve25519-dalek"
+version = "4.0.0-pre.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4033478fbf70d6acf2655ac70da91ee65852d69daf7a67bf7a2f518fb47aafcf"
+dependencies = [
+ "byteorder",
+ "digest 0.9.0",
+ "rand_core",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "der"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
+dependencies = [
+ "const-oid",
+]
+
+[[package]]
+name = "derive-where"
+version = "1.0.0-rc.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d322f2907b2abad3117790c1a54d8f2d64574ba0fbea54cb6c6e66a0e50d99a4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.107",
+]
+
[[package]]
name = "derive_more"
version = "0.99.17"
@@ -215,12 +324,33 @@
[[package]]
name = "digest"
-version = "0.10.6"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
]
[[package]]
@@ -229,6 +359,25 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+[[package]]
+name = "elliptic-curve"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
+dependencies = [
+ "base16ct",
+ "crypto-bigint",
+ "der",
+ "digest 0.10.7",
+ "ff",
+ "generic-array",
+ "group",
+ "rand_core",
+ "sec1",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "fastrand"
version = "1.8.0"
@@ -238,6 +387,16 @@
"instant",
]
+[[package]]
+name = "ff"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160"
+dependencies = [
+ "rand_core",
+ "subtle",
+]
+
[[package]]
name = "fixedbitset"
version = "0.4.2"
@@ -339,6 +498,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
+ "serde",
"typenum",
"version_check",
]
@@ -354,6 +514,17 @@
"wasi",
]
+[[package]]
+name = "group"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7"
+dependencies = [
+ "ff",
+ "rand_core",
+ "subtle",
+]
+
[[package]]
name = "h2"
version = "0.3.17"
@@ -400,6 +571,24 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+[[package]]
+name = "hkdf"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
+dependencies = [
+ "hmac",
+]
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest 0.10.7",
+]
+
[[package]]
name = "http"
version = "0.2.8"
@@ -593,6 +782,28 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+[[package]]
+name = "opaque-ke"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d410412d23781909d90c3900c5783e830586765f2277bccc78167da8af81a5"
+dependencies = [
+ "argon2",
+ "curve25519-dalek",
+ "derive-where",
+ "digest 0.10.7",
+ "displaydoc",
+ "elliptic-curve",
+ "generic-array",
+ "hkdf",
+ "hmac",
+ "rand",
+ "serde",
+ "subtle",
+ "voprf",
+ "zeroize",
+]
+
[[package]]
name = "openssl"
version = "0.10.48"
@@ -632,6 +843,17 @@
"vcpkg",
]
+[[package]]
+name = "password-hash"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
+dependencies = [
+ "base64ct",
+ "rand_core",
+ "subtle",
+]
+
[[package]]
name = "percent-encoding"
version = "2.2.0"
@@ -859,6 +1081,19 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+[[package]]
+name = "sec1"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928"
+dependencies = [
+ "base16ct",
+ "der",
+ "generic-array",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "semver"
version = "1.0.16"
@@ -904,7 +1139,7 @@
dependencies = [
"cfg-if",
"cpufeatures",
- "digest",
+ "digest 0.10.7",
]
[[package]]
@@ -915,7 +1150,7 @@
dependencies = [
"cfg-if",
"cpufeatures",
- "digest",
+ "digest 0.10.7",
]
[[package]]
@@ -937,6 +1172,12 @@
"winapi",
]
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
[[package]]
name = "syn"
version = "1.0.107"
@@ -1122,6 +1363,26 @@
"tracing-futures",
]
+[[package]]
+name = "tonic"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
+dependencies = [
+ "base64 0.21.2",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "percent-encoding",
+ "pin-project",
+ "tokio-stream",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "tonic-build"
version = "0.8.4"
@@ -1318,6 +1579,25 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+[[package]]
+name = "voprf"
+version = "0.4.0-pre.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "081acbe8fcf05d5e8e2aad8ef3d40e02eddeaec07c75a9770d862a0fc0874322"
+dependencies = [
+ "curve25519-dalek",
+ "derive-where",
+ "digest 0.10.7",
+ "displaydoc",
+ "elliptic-curve",
+ "generic-array",
+ "rand_core",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "want"
version = "0.3.0"
@@ -1334,6 +1614,60 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
+
[[package]]
name = "which"
version = "4.3.0"
@@ -1423,3 +1757,23 @@
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
+
+[[package]]
+name = "zeroize"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.15",
+]
diff --git a/services/commtest/Cargo.toml b/services/commtest/Cargo.toml
--- a/services/commtest/Cargo.toml
+++ b/services/commtest/Cargo.toml
@@ -5,6 +5,7 @@
license = "BSD-3-Clause"
[dependencies]
+comm-opaque2 = { path = "../../shared/comm-opaque2" }
tonic = "0.8"
tokio = { version = "1.24", features = ["macros", "rt-multi-thread"] }
prost = "0.11"
@@ -24,6 +25,7 @@
url = "2.3.1"
futures-util = "0.3.28"
serde_json = "1.0.96"
+rand = "0.8.5"
[build-dependencies]
tonic-build = "0.8"
diff --git a/services/commtest/tests/identity_access_tokens_tests.rs b/services/commtest/tests/identity_access_tokens_tests.rs
new file mode 100644
--- /dev/null
+++ b/services/commtest/tests/identity_access_tokens_tests.rs
@@ -0,0 +1,115 @@
+mod proto {
+ tonic::include_proto!("identity.client");
+}
+use comm_opaque2::client::Registration;
+use proto::identity_client_service_client::IdentityClientServiceClient;
+use proto::{
+ DeviceKeyUpload, IdentityKeyInfo, PreKey, RegistrationFinishRequest,
+ RegistrationStartRequest, VerifyUserAccessTokenRequest,
+};
+use rand::{distributions::Alphanumeric, Rng};
+
+struct DeviceInfo {
+ pub username: String,
+ pub user_id: String,
+ pub device_id: String,
+ pub access_token: String,
+}
+
+async fn create_device() -> DeviceInfo {
+ let password = "pass";
+ let username: String = rand::thread_rng()
+ .sample_iter(&Alphanumeric)
+ .take(7)
+ .map(char::from)
+ .collect();
+
+ // TODO: Generate dynamic valid olm account info
+ let example_payload = r#"{\"notificationIdentityPublicKeys\":{\"curve25519\":\"DYmV8VdkjwG/VtC8C53morogNJhpTPT/4jzW0/cxzQo\",\"ed25519\":\"D0BV2Y7Qm36VUtjwyQTJJWYAycN7aMSJmhEsRJpW2mk\"},\"primaryIdentityPublicKeys\":{\"curve25519\":\"Y4ZIqzpE1nv83kKGfvFP6rifya0itRg2hifqYtsISnk\",\"ed25519\":\"cSlL+VLLJDgtKSPlIwoCZg0h0EmHlQoJC08uV/O+jvg\"}}"#;
+ // The ed25519 value from the olm payload
+ let device_id = r#"cSlL+VLLJDgtKSPlIwoCZg0h0EmHlQoJC08uV/O+jvg"#;
+
+ let mut client_registration = Registration::new();
+ let opaque_registration_request =
+ client_registration.start(&password).unwrap();
+ let registration_start_request = RegistrationStartRequest {
+ opaque_registration_request,
+ username: username.to_string(),
+ device_key_upload: Some(DeviceKeyUpload {
+ device_key_info: Some(IdentityKeyInfo {
+ payload: example_payload.to_string(),
+ payload_signature: "foo".to_string(),
+ social_proof: None,
+ }),
+ content_upload: Some(PreKey {
+ pre_key: "content_prekey".to_string(),
+ pre_key_signature: "content_prekey_sig".to_string(),
+ }),
+ notif_upload: Some(PreKey {
+ pre_key: "notif_prekey".to_string(),
+ pre_key_signature: "notif_prekey_sig".to_string(),
+ }),
+ onetime_content_prekeys: Vec::new(),
+ onetime_notif_prekeys: Vec::new(),
+ }),
+ };
+
+ // TODO: allow endpoint to be configured
+ let mut identity_client =
+ IdentityClientServiceClient::connect("http://127.0.0.1:50054")
+ .await
+ .expect("Couldn't connect to identitiy service");
+
+ let registration_start_response = identity_client
+ .register_password_user_start(registration_start_request)
+ .await
+ .unwrap()
+ .into_inner();
+
+ let opaque_registration_upload = client_registration
+ .finish(
+ &password,
+ &registration_start_response.opaque_registration_response,
+ )
+ .unwrap();
+ let registration_finish_request = RegistrationFinishRequest {
+ session_id: registration_start_response.session_id,
+ opaque_registration_upload,
+ };
+
+ let registration_finish_response = identity_client
+ .register_password_user_finish(registration_finish_request)
+ .await
+ .unwrap()
+ .into_inner();
+
+ return DeviceInfo {
+ username: username.to_string(),
+ device_id: device_id.to_string(),
+ user_id: registration_finish_response.user_id,
+ access_token: registration_finish_response.access_token,
+ };
+}
+
+#[tokio::test]
+async fn verify_access_token() {
+ let device_info = create_device().await;
+
+ let mut identity_client =
+ IdentityClientServiceClient::connect("http://127.0.0.1:50054")
+ .await
+ .expect("Couldn't connect to identitiy service");
+
+ let verify_request = VerifyUserAccessTokenRequest {
+ user_id: device_info.user_id,
+ signing_public_key: device_info.device_id,
+ access_token: device_info.access_token,
+ };
+
+ let response = identity_client
+ .verify_user_access_token(verify_request)
+ .await
+ .unwrap();
+
+ assert_eq!(response.into_inner().token_valid, true);
+}
diff --git a/services/identity/src/client_service.rs b/services/identity/src/client_service.rs
--- a/services/identity/src/client_service.rs
+++ b/services/identity/src/client_service.rs
@@ -766,20 +766,16 @@
request: tonic::Request<VerifyUserAccessTokenRequest>,
) -> Result<tonic::Response<VerifyUserAccessTokenResponse>, tonic::Status> {
let message = request.into_inner();
- let token_valid = match self
+ let token_valid = self
.client
- .get_access_token_data(message.user_id, message.signing_public_key)
+ .verify_access_token(
+ message.user_id,
+ message.signing_public_key,
+ message.access_token,
+ )
.await
- {
- Ok(Some(access_token_data)) => {
- constant_time_eq(
- access_token_data.access_token.as_bytes(),
- message.access_token.as_bytes(),
- ) && access_token_data.is_valid()
- }
- Ok(None) => false,
- Err(e) => return Err(handle_db_error(e)),
- };
+ .map_err(handle_db_error)?;
+
let response = Response::new(VerifyUserAccessTokenResponse { token_valid });
Ok(response)
}
diff --git a/services/identity/src/database.rs b/services/identity/src/database.rs
--- a/services/identity/src/database.rs
+++ b/services/identity/src/database.rs
@@ -1,3 +1,4 @@
+use constant_time_eq::constant_time_eq;
use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str::FromStr;
@@ -369,6 +370,26 @@
}
}
+ pub async fn verify_access_token(
+ &self,
+ user_id: String,
+ signing_public_key: String,
+ access_token_to_verify: String,
+ ) -> Result<bool, Error> {
+ let is_valid = self
+ .get_access_token_data(user_id, signing_public_key)
+ .await?
+ .map(|access_token_data| {
+ constant_time_eq(
+ access_token_data.access_token.as_bytes(),
+ access_token_to_verify.as_bytes(),
+ ) && access_token_data.is_valid()
+ })
+ .unwrap_or(false);
+
+ Ok(is_valid)
+ }
+
pub async fn put_access_token_data(
&self,
access_token_data: AccessTokenData,

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 10, 1:18 AM (13 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2842819
Default Alt Text
D8094.id27630.diff (19 KB)

Event Timeline