Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: move operations to nested Auth and User structs #30

Merged
merged 10 commits into from
Dec 19, 2024
37 changes: 12 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,6 @@ Find more details about Passkey Flex on our [Passkey Flex Documentation](https:/

## API Reference

### Retrieve App Info

To retrieve information about the app, use the `get_app` method.

```rust
use passage_flex::PassageFlex;

let passage_flex = PassageFlex::new(
std::env::var("PASSAGE_APP_ID").unwrap(),
std::env::var("PASSAGE_API_KEY").unwrap(),
);

let app_info = passage_flex.get_app().await.unwrap();
println!("{}", app_info.auth_origin);
```

### Create a Registration Transaction

To create a transaction to start a user passkey registration, use the `create_register_transaction` method.
Expand All @@ -92,6 +76,7 @@ let passkey_display_name =
"the label for the user's passkey that they will see when logging in".to_string();

let transaction = passage_flex
.auth
.create_register_transaction(external_id, passkey_display_name)
.await
.unwrap();
Expand All @@ -112,6 +97,7 @@ let passage_flex = PassageFlex::new(
let external_id = "a unique immutable string that represents your user".to_string();

let transaction = passage_flex
.auth
.create_authenticate_transaction(external_id)
.await
.unwrap();
Expand All @@ -132,7 +118,7 @@ let passage_flex = PassageFlex::new(
let nonce =
"a unique single-use value received from the client after a passkey ceremony".to_string();

match passage_flex.verify_nonce(nonce).await {
match passage_flex.auth.verify_nonce(nonce).await {
Ok(external_id) => {
// use external_id to do things like generate and send your own auth token
}
Expand All @@ -144,7 +130,7 @@ match passage_flex.verify_nonce(nonce).await {

## Retrieve User Info

To retrieve information about a user by their external ID -- which is the unique, immutable ID you supply to associate the Passage user with your user -- use the `get_user` method.
To retrieve information about a user by their external ID -- which is the unique, immutable ID you supply to associate the Passage user with your user -- use the `get` method.

```rust
use passage_flex::PassageFlex;
Expand All @@ -158,13 +144,13 @@ let passage_flex = PassageFlex::new(
let external_id = your_user.id;

// get user info
let user_info = passage_flex.get_user(external_id).await.unwrap();
println!("{:?}", user_info.webauthn_devices);
let passage_user = passage_flex.user.get(external_id).await.unwrap();
println!("{:?}", passage_user.webauthn_devices);
```

## Retrieve a user's passkey devices

To retrieve information about a user's passkey devices, use the `get_devices` method.
To retrieve information about a user's passkey devices, use the `list_devices` method.

```rust
use passage_flex::PassageFlex;
Expand All @@ -177,8 +163,8 @@ let passage_flex = PassageFlex::new(
// this is the same value used when creating a transaction
let external_id = your_user.id;

// get devices
let passkey_devices = passage_flex.get_devices(external_id).await.unwrap();
// list devices
let passkey_devices = passage_flex.user.list_devices(external_id).await.unwrap();
for device in passkey_devices {
println!("{}", device.usage_count);
}
Expand All @@ -201,8 +187,8 @@ let passage_flex = PassageFlex::new(
let external_id = your_user.id;
let last_year = Utc::now().naive_utc().date() - Duration::days(365);

// get devices
let passkey_devices = passage_flex.get_devices(external_id.clone()).await.unwrap();
// list devices
let passkey_devices = passage_flex.user.list_devices(external_id.clone()).await.unwrap();

for device in passkey_devices {
// revoke old devices that haven't been used in the last year
Expand All @@ -211,6 +197,7 @@ for device in passkey_devices {

if last_login_at_parsed < last_year {
if let Err(err) = passage_flex
.user
.revoke_device(external_id.clone(), device.id)
.await
{
Expand Down
141 changes: 141 additions & 0 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use crate::openapi::apis::configuration::Configuration;
use crate::openapi::apis::{authenticate_api, transactions_api};
use crate::Error;

pub struct Auth {
pub(crate) configuration: Configuration,
}

impl Auth {
/// Creates a new instance of the `Auth` struct.
pub fn new(configuration: Configuration) -> Self {
Self { configuration }
}

/// Creates a transaction to start a user's registration process.
///
/// # Arguments
///
/// * `external_id` - A unique, immutable string that represents the user.
/// * `passkey_display_name` - The label for the user's passkey that they will see when logging in.
///
/// # Returns
///
/// A `Result` containing the transaction ID as a string or an `Error`.
///
/// # Examples
///
/// ```ignore
/// use passage_flex::PassageFlex;
///
/// let passage_flex = PassageFlex::new(
/// std::env::var("PASSAGE_APP_ID").unwrap(),
/// std::env::var("PASSAGE_API_KEY").unwrap(),
/// );
///
/// let transaction = passage_flex
/// .auth
/// .create_register_transaction(
/// "00000000-0000-0000-0000-000000000001".to_string(),
/// "[email protected]".to_string(),
/// )
/// .await
/// .unwrap();
/// ```
pub async fn create_register_transaction(
&self,
external_id: String,
passkey_display_name: String,
) -> Result<String, Error> {
transactions_api::create_register_transaction(
&self.configuration,
crate::openapi::models::CreateTransactionRegisterRequest {
external_id,
passkey_display_name,
},
)
.await
.map(|response| response.transaction_id)
.map_err(Into::into)
}

/// Creates a transaction to start a user's authentication process.
///
/// # Arguments
///
/// * `external_id` - A unique, immutable string that represents the user.
///
/// # Returns
///
/// A `Result` containing the transaction ID as a string or an `Error`.
///
/// # Examples
///
/// ```ignore
/// use passage_flex::PassageFlex;
///
/// let passage_flex = PassageFlex::new(
/// std::env::var("PASSAGE_APP_ID").unwrap(),
/// std::env::var("PASSAGE_API_KEY").unwrap(),
/// );
///
/// let transaction = passage_flex
/// .auth
/// .create_authenticate_transaction(
/// "00000000-0000-0000-0000-000000000001".to_string(),
/// )
/// .await
/// .unwrap();
/// ```
pub async fn create_authenticate_transaction(
&self,
external_id: String,
) -> Result<String, Error> {
transactions_api::create_authenticate_transaction(
&self.configuration,
crate::openapi::models::CreateTransactionAuthenticateRequest { external_id },
)
.await
.map(|response| response.transaction_id)
.map_err(Into::into)
}

/// Verifies the nonce received from a WebAuthn registration or authentication ceremony.
///
/// # Arguments
///
/// * `nonce` - The nonce string to be verified.
///
/// # Returns
///
/// A `Result` containing the external ID as a string or an `Error`.
///
/// # Examples
///
/// ```ignore
/// use passage_flex::PassageFlex;
///
/// let passage_flex = PassageFlex::new(
/// std::env::var("PASSAGE_APP_ID").unwrap(),
/// std::env::var("PASSAGE_API_KEY").unwrap(),
/// );
///
/// match passage_flex.auth.verify_nonce("01234567890123456789".to_string()).await {
/// Ok(external_id) => {
/// // use external_id to do things like generate and send your own auth token
/// }
/// Err(err) => {
/// // nonce was invalid or unable to be verified
/// }
/// }
/// ```
pub async fn verify_nonce(&self, nonce: String) -> Result<String, Error> {
authenticate_api::authenticate_verify_nonce(
&self.configuration,
crate::openapi::models::Nonce { nonce },
)
.await
.map(|response| response.external_id)
.map_err(Into::into)
}
}
15 changes: 1 addition & 14 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::openapi::apis::{
apps_api, authenticate_api, transactions_api, user_devices_api, users_api,
};
use crate::openapi::apis::{authenticate_api, transactions_api, user_devices_api, users_api};
use crate::Error;

// This function converts an openapi error into a crate error
Expand All @@ -16,17 +14,6 @@ fn convert_error<Src>(error: crate::openapi::apis::Error<Src>, map_fn: fn(Src) -
}
}

impl From<crate::openapi::apis::Error<apps_api::GetAppError>> for Error {
fn from(e: crate::openapi::apis::Error<apps_api::GetAppError>) -> Self {
convert_error(e, |e| match e {
apps_api::GetAppError::Status401(model) => model.into(),
apps_api::GetAppError::Status404(model) => model.into(),
apps_api::GetAppError::Status500(model) => model.into(),
apps_api::GetAppError::UnknownValue(v) => Error::Other(v.to_string()),
})
}
}

impl From<crate::openapi::apis::Error<transactions_api::CreateRegisterTransactionError>> for Error {
fn from(
e: crate::openapi::apis::Error<transactions_api::CreateRegisterTransactionError>,
Expand Down
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
//! std::env::var("PASSAGE_API_KEY").unwrap(),
//! );
//!
//! let app_info = passage_flex.get_app().await.unwrap();
//! println!("{}", app_info.auth_origin);
//! let passage_user = passage_flex.user.get(external_id).await.unwrap();
//! println!("{:?}", passage_user.webauthn_devices);
//! ```

use std::fmt;
Expand Down Expand Up @@ -62,5 +62,7 @@ pub mod models;
#[rustfmt::skip]
pub mod openapi;

pub mod auth;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this would also expose the auth and user structs to the developer

pub mod passage_flex;
pub mod user;
pub use passage_flex::PassageFlex;
11 changes: 0 additions & 11 deletions src/models/app_info.rs

This file was deleted.

6 changes: 2 additions & 4 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
mod app_info;
mod user_info;
mod passage_user;

pub use app_info::AppInfo;
pub use user_info::UserInfo;
pub use passage_user::PassageUser;
2 changes: 1 addition & 1 deletion src/models/user_info.rs → src/models/passage_user.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct UserInfo {
pub struct PassageUser {
#[serde(rename = "created_at")]
pub created_at: String,
/// The external ID of the user. Only set if the user was created in a Flex app.
Expand Down
Loading
Loading