Skip to content

Commit

Permalink
Add an example for device code oidc login
Browse files Browse the repository at this point in the history
  • Loading branch information
kilimnik committed Jan 11, 2025
1 parent a69a422 commit f1c4a94
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 0 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions examples/device-code/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "example-device-code-login"
version = "0.1.0"
edition = "2021"
publish = false
license = "Apache-2.0"

[[bin]]
name = "example-device-code-login"
test = false

[dependencies]
anyhow = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
clap = { version = "4.0.15", features = ["derive"] }
tracing-subscriber = { workspace = true }
url = "2.3.1"
dirs = "5.0.1"

[dependencies.matrix-sdk]
# when copy-pasting this, please use a git dependency or make sure that you
# have copied the example as it was at the time of the release you use.
path = "../../crates/matrix-sdk"
features = ["experimental-oidc"]

[package.metadata.release]
release = false
119 changes: 119 additions & 0 deletions examples/device-code/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use std::collections::HashMap;

use anyhow::Result;
use clap::Parser;
use matrix_sdk::{
oidc::{
registrations::OidcRegistrations,
types::{
iana::oauth::OAuthClientAuthenticationMethod,
oidc::ApplicationType,
registration::{ClientMetadata, Localized, VerifiedClientMetadata},
requests::GrantType,
},
},
Client,
};
use url::Url;

/// A command line example showcasing how to login using a device code.
///
/// Another device, will verify the device code.
#[derive(Parser, Debug)]
struct Cli {
/// Set the homeserver that should be used for authentication.
#[clap(long, required = true)]
homeserver: Url,

/// Add extra scopes to the request.
#[clap(long)]
extra_scopes: Option<Vec<String>>,

/// Enable verbose logging output.
#[clap(short, long, action)]
verbose: bool,
}

/// Generate the OIDC client metadata.
///
/// For simplicity, we use most of the default values here, but usually this
/// should be adapted to the provider metadata to make interactions as secure as
/// possible, for example by using the most secure signing algorithms supported
/// by the provider.
fn client_metadata() -> VerifiedClientMetadata {
let client_uri = Url::parse("https://github.com/matrix-org/matrix-rust-sdk")
.expect("Couldn't parse client URI");

ClientMetadata {
// This is a native application (in contrast to a web application, that runs in a browser).
application_type: Some(ApplicationType::Native),
// Native clients should be able to register the loopback interface and then point to any
// port when needing a redirect URI. An alternative is to use a custom URI scheme registered
// with the OS.
redirect_uris: None,
// We are going to use the Authorization Code flow, and of course we want to be able to
// refresh our access token.
grant_types: Some(vec![GrantType::RefreshToken, GrantType::DeviceCode]),
// A native client shouldn't use authentication as the credentials could be intercepted.
// Other protections are in place for the different requests.
token_endpoint_auth_method: Some(OAuthClientAuthenticationMethod::None),
// The following fields should be displayed in the OIDC provider interface as part of the
// process to get the user's consent. It means that these should contain real data so the
// user can make sure that they allow the proper application.
// We are cheating here because this is an example.
client_name: Some(Localized::new("matrix-rust-sdk-device-code-login".to_owned(), [])),
contacts: Some(vec!["[email protected]".to_owned()]),
client_uri: Some(Localized::new(client_uri.clone(), [])),
policy_uri: Some(Localized::new(client_uri.clone(), [])),
tos_uri: Some(Localized::new(client_uri, [])),
..Default::default()
}
.validate()
.unwrap()
}

#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();

if cli.verbose {
tracing_subscriber::fmt::init();
}

let server_name = cli.homeserver;
let client = Client::builder().server_name_or_homeserver_url(server_name).build().await?;

let metadata = client_metadata();

let data_dir = dirs::data_dir().expect("no data_dir directory found");
let registrations_file = data_dir.join("matrix_sdk/oidc").join("registrations.json");

let static_registrations = HashMap::new();

let registrations =
OidcRegistrations::new(&registrations_file, client_metadata(), static_registrations)?;

let oidc = client.oidc();

let mut login_device_code = oidc.login_with_device_code(metadata, registrations);

let auth_grant_response = login_device_code.device_code_for_login(cli.extra_scopes).await?;

println!(
"Log in using this {}",
auth_grant_response.verification_uri_complete().unwrap().clone().into_secret()
);
println!(
"You can also go to {} and type in the code {}",
auth_grant_response.verification_uri(),
auth_grant_response.user_code().clone().into_secret()
);

login_device_code.wait_finish_login().await?;

let user_id = client.user_id().unwrap();

println!("Successfully logged in as {user_id} using the device code");

Ok(())
}

0 comments on commit f1c4a94

Please sign in to comment.