Skip to content

Commit 424ddbf

Browse files
committed
Add an example for device code oidc login
1 parent b266088 commit 424ddbf

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

Diff for: Cargo.lock

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: examples/device-code/Cargo.toml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
name = "example-device-code-login"
3+
version = "0.1.0"
4+
edition = "2021"
5+
publish = false
6+
license = "Apache-2.0"
7+
8+
[[bin]]
9+
name = "example-device-code-login"
10+
test = false
11+
12+
[dependencies]
13+
anyhow = { workspace = true }
14+
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
15+
clap = { version = "4.0.15", features = ["derive"] }
16+
tracing-subscriber = { workspace = true }
17+
url = "2.3.1"
18+
dirs = "5.0.1"
19+
20+
[dependencies.matrix-sdk]
21+
# when copy-pasting this, please use a git dependency or make sure that you
22+
# have copied the example as it was at the time of the release you use.
23+
path = "../../crates/matrix-sdk"
24+
features = ["experimental-oidc"]
25+
26+
[package.metadata.release]
27+
release = false

Diff for: examples/device-code/src/main.rs

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use std::collections::HashMap;
2+
3+
use anyhow::Result;
4+
use clap::Parser;
5+
use matrix_sdk::{
6+
oidc::{
7+
registrations::OidcRegistrations,
8+
types::{
9+
iana::oauth::OAuthClientAuthenticationMethod,
10+
oidc::ApplicationType,
11+
registration::{ClientMetadata, Localized, VerifiedClientMetadata},
12+
requests::GrantType, scope::ScopeToken,
13+
},
14+
},
15+
Client,
16+
};
17+
use url::Url;
18+
19+
/// A command line example showcasing how to login using a device code.
20+
///
21+
/// Another device, will verify the device code.
22+
#[derive(Parser, Debug)]
23+
struct Cli {
24+
/// Set the homeserver that should be used for authentication.
25+
#[clap(long, required = true)]
26+
homeserver: Url,
27+
28+
/// Add extra scopes to the request.
29+
#[clap(long)]
30+
custom_scopes: Option<Vec<ScopeToken>>,
31+
32+
/// Enable verbose logging output.
33+
#[clap(short, long, action)]
34+
verbose: bool,
35+
}
36+
37+
/// Generate the OIDC client metadata.
38+
///
39+
/// For simplicity, we use most of the default values here, but usually this
40+
/// should be adapted to the provider metadata to make interactions as secure as
41+
/// possible, for example by using the most secure signing algorithms supported
42+
/// by the provider.
43+
fn client_metadata() -> VerifiedClientMetadata {
44+
let client_uri = Url::parse("https://github.com/matrix-org/matrix-rust-sdk")
45+
.expect("Couldn't parse client URI");
46+
47+
ClientMetadata {
48+
// This is a native application (in contrast to a web application, that runs in a browser).
49+
application_type: Some(ApplicationType::Native),
50+
// Native clients should be able to register the loopback interface and then point to any
51+
// port when needing a redirect URI. An alternative is to use a custom URI scheme registered
52+
// with the OS.
53+
redirect_uris: None,
54+
// We are going to use the Authorization Code flow, and of course we want to be able to
55+
// refresh our access token.
56+
grant_types: Some(vec![GrantType::RefreshToken, GrantType::DeviceCode]),
57+
// A native client shouldn't use authentication as the credentials could be intercepted.
58+
// Other protections are in place for the different requests.
59+
token_endpoint_auth_method: Some(OAuthClientAuthenticationMethod::None),
60+
// The following fields should be displayed in the OIDC provider interface as part of the
61+
// process to get the user's consent. It means that these should contain real data so the
62+
// user can make sure that they allow the proper application.
63+
// We are cheating here because this is an example.
64+
client_name: Some(Localized::new("matrix-rust-sdk-device-code-login".to_owned(), [])),
65+
contacts: Some(vec!["[email protected]".to_owned()]),
66+
client_uri: Some(Localized::new(client_uri.clone(), [])),
67+
policy_uri: Some(Localized::new(client_uri.clone(), [])),
68+
tos_uri: Some(Localized::new(client_uri, [])),
69+
..Default::default()
70+
}
71+
.validate()
72+
.unwrap()
73+
}
74+
75+
#[tokio::main]
76+
async fn main() -> Result<()> {
77+
let cli = Cli::parse();
78+
79+
if cli.verbose {
80+
tracing_subscriber::fmt::init();
81+
}
82+
83+
let server_name = cli.homeserver;
84+
let client = Client::builder().server_name_or_homeserver_url(server_name).build().await?;
85+
86+
let metadata = client_metadata();
87+
88+
let data_dir = dirs::data_dir().expect("no data_dir directory found");
89+
let registrations_file = data_dir.join("matrix_sdk/oidc").join("registrations.json");
90+
91+
let static_registrations = HashMap::new();
92+
93+
let registrations =
94+
OidcRegistrations::new(&registrations_file, client_metadata(), static_registrations)?;
95+
96+
let oidc = client.oidc();
97+
98+
let mut login_device_code = oidc.login_with_device_code(metadata, registrations);
99+
100+
let auth_grant_response = login_device_code.device_code_for_login(cli.custom_scopes).await?;
101+
102+
println!(
103+
"Log in using this {}",
104+
auth_grant_response.verification_uri_complete().unwrap().clone().into_secret()
105+
);
106+
println!(
107+
"You can also go to {} and type in the code {}",
108+
auth_grant_response.verification_uri(),
109+
auth_grant_response.user_code().clone().into_secret()
110+
);
111+
112+
login_device_code.wait_finish_login().await?;
113+
114+
let user_id = client.user_id().unwrap();
115+
116+
println!("Successfully logged in as {user_id} using the device code");
117+
118+
Ok(())
119+
}

0 commit comments

Comments
 (0)