Skip to content

Commit

Permalink
Merge pull request #71 from go-bazzinga/rupansh/login-iframe
Browse files Browse the repository at this point in the history
feat: integrate auth metadata api
  • Loading branch information
rupansh-sekar-yral authored Feb 19, 2024
2 parents 53333d9 + 063f94c commit 13dba67
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 103 deletions.
4 changes: 2 additions & 2 deletions did/individual_user_template.did
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ type UserProfileUpdateDetailsFromFrontend = record {
profile_picture_url : opt text;
display_name : opt text;
};
service : {
service : (IndividualUserTemplateInitArgs) -> {
add_post_v2 : (PostDetailsFromFrontend) -> (Result);
backup_data_to_backup_canister : (principal, principal) -> ();
bet_on_currently_viewing_post : (PlaceBetArg) -> (Result_1);
Expand Down Expand Up @@ -344,4 +344,4 @@ service : {
update_profiles_that_follow_me_toggle_list_with_specified_profile : (
FollowerArg,
) -> (Result_2);
}
}
5 changes: 3 additions & 2 deletions did/platform_orchestrator.did
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ type WasmType = variant {
SubnetOrchestratorWasm;
};
service : (PlatformOrchestratorInitArgs) -> {
get_next_available_subnet : () -> (principal) query;
get_all_available_subnet_orchestrators : () -> (vec principal) query;
get_all_subnet_orchestrators : () -> (vec principal) query;
get_subnet_last_upgrade_status : () -> (CanisterUpgradeStatus) query;
get_version : () -> (text) query;
provision_subnet_orchestrator_canister : (principal) -> (Result);
subnet_orchestrator_maxed_out : () -> ();
upgrade_canister : (UpgradeCanisterArg) -> (Result_1);
upload_wasms : (WasmType, vec nat8) -> (Result_1);
}
}
2 changes: 1 addition & 1 deletion did/post_cache.did
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type TopPostsFetchError = variant {
InvalidBoundsPassed;
ExceededMaxNumberOfItemsAllowedInOneRequest;
};
service : {
service : (PostCacheInitArgs) -> {
get_cycle_balance : () -> (nat) query;
get_top_posts_aggregated_from_canisters_on_this_network_for_home_feed : (
nat64,
Expand Down
9 changes: 8 additions & 1 deletion did/user_index.did
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ type CanisterStatusResponse = record {
memory_size : nat;
cycles : nat;
settings : DefiniteCanisterSettings;
query_stats : QueryStats;
idle_cycles_burned_per_day : nat;
module_hash : opt vec nat8;
};
Expand All @@ -27,6 +28,12 @@ type KnownPrincipalType = variant {
CanisterIdSnsGovernance;
UserIdGlobalSuperAdmin;
};
type QueryStats = record {
response_payload_bytes_total : nat;
num_instructions_total : nat;
num_calls_total : nat;
request_payload_bytes_total : nat;
};
type RejectionCode = variant {
NoError;
CanisterError;
Expand Down Expand Up @@ -70,7 +77,7 @@ type UserIndexInitArgs = record {
version : text;
access_control_map : opt vec record { principal; vec UserAccessRole };
};
service : {
service : (UserIndexInitArgs) -> {
are_signups_enabled : () -> (bool) query;
backup_all_individual_user_canisters : () -> ();
create_pool_of_individual_user_available_canisters : (text, vec nat8) -> (
Expand Down
3 changes: 2 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
upload::UploadPostPage,
},
state::{
auth::AuthState,
auth::{AuthClient, AuthState},
canisters::{do_canister_auth, Canisters},
},
utils::MockPartialEq,
Expand All @@ -33,6 +33,7 @@ pub fn App() -> impl IntoView {
provide_context(PostViewCtx::default());
let auth_state = AuthState::default();
provide_context(auth_state.clone());
provide_context(AuthClient::default());
provide_context(Resource::local(
move || MockPartialEq(auth_state.identity.get()),
|auth| do_canister_auth(auth.0),
Expand Down
5 changes: 4 additions & 1 deletion src/component/auth_provider.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{
consts::AUTH_URL,
state::auth::{auth_state, DelegationIdentity, SessionResponse},
state::auth::{
auth_state,
types::{DelegationIdentity, SessionResponse},
},
};
use leptos::*;
use leptos_use::{use_event_listener, use_window};
Expand Down
2 changes: 1 addition & 1 deletion src/component/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use reqwest::Url;

use crate::{
consts::{self, ACCOUNT_CONNECTED_STORE},
state::auth::{auth_state, SessionResponse},
state::auth::{auth_state, types::SessionResponse},
};

#[component]
Expand Down
26 changes: 10 additions & 16 deletions src/page/profile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use leptos_icons::*;
use leptos_router::*;

use crate::{
component::spinner::FullScreenSpinner, state::canisters::unauth_canisters,
component::spinner::FullScreenSpinner,
state::{auth::auth_client, canisters::unauth_canisters},
utils::profile::ProfileDetails,
};

Expand Down Expand Up @@ -109,28 +110,21 @@ fn ProfileViewInner(user: ProfileDetails, user_canister: Principal) -> impl Into
#[component]
pub fn ProfileView() -> impl IntoView {
let params = use_params::<ProfileParams>();
let principal_or_username = move || {
let principal = move || {
params.with(|p| {
let ProfileParams { id } = p.as_ref().ok()?;

let res = Principal::from_text(id).map_err(|_| id.clone());
Some(res)
Principal::from_text(id).ok()
})
};

let user_details = create_resource(principal_or_username, |principal_or_username| async move {
let user_details = create_resource(principal, |principal| async move {
let canisters = unauth_canisters();
let user_index = canisters.user_index();
let user_canister = match principal_or_username? {
Ok(p) => user_index
.get_user_canister_id_from_user_principal_id(p)
.await
.ok()??,
Err(u) => user_index
.get_user_canister_id_from_unique_user_name(u)
.await
.ok()??,
};
let auth = auth_client();
let user_canister = auth
.get_individual_canister_by_user_principal(principal?)
.await
.ok()??;
let user = canisters.individual_user(user_canister);
let user_details = user.get_profile_details().await.ok()?;
Some((user_details.into(), user_canister))
Expand Down
113 changes: 113 additions & 0 deletions src/state/auth/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
pub mod types;

use std::num::ParseIntError;

use ic_agent::{export::Principal, identity::DelegatedIdentity};

use leptos::{create_effect, create_signal, expect_context, Effect, ReadSignal, RwSignal};
use leptos_use::storage::{use_local_storage, StringCodec};
use thiserror::Error;

use crate::consts::{ACCOUNT_CONNECTED_STORE, AUTH_URL};
use types::{DelegationIdentity, SessionResponse, UserDetails};

#[derive(Error, Debug, Clone)]
pub enum AuthError {
#[error("Invalid Secret Key")]
InvalidSecretKey(#[from] k256::elliptic_curve::Error),
#[error("Invalid expiry")]
InvalidExpiry(#[from] ParseIntError),
#[error("reqwest error: {0}")]
Reqwest(String),
}

impl From<reqwest::Error> for AuthError {
fn from(e: reqwest::Error) -> Self {
AuthError::Reqwest(e.to_string())
}
}

#[derive(Default, Clone)]
pub struct AuthClient {
client: reqwest::Client,
}

impl AuthClient {
pub async fn generate_session(&self) -> Result<DelegatedIdentity, AuthError> {
let resp: SessionResponse = self
.client
.post(AUTH_URL.join("api/generate_session").unwrap())
.send()
.await?
.json()
.await?;
resp.delegation_identity.try_into()
}

pub async fn update_user_metadata(
&self,
id: DelegationIdentity,
user_canister: Principal,
username: String,
) -> Result<(), AuthError> {
let details = UserDetails {
delegation_identity: id,
user_canister_id: user_canister.to_text(),
user_name: username,
};
let res = self
.client
.post(AUTH_URL.join("rest_api/update_user_metadata").unwrap())
.json(&details)
.send()
.await?;
if res.status().is_success() {
Ok(())
} else {
Err(AuthError::Reqwest(res.text().await?))
}
}

pub async fn get_individual_canister_by_user_principal(
&self,
user_principal: Principal,
) -> Result<Option<Principal>, AuthError> {
let res = self
.client
.post(AUTH_URL.join("rest_api/get_user_canister").unwrap())
.json(&user_principal.to_text())
.send()
.await?
.text()
.await?;
Ok(Principal::from_text(res).ok())
}
}

pub fn auth_client() -> AuthClient {
expect_context()
}

#[derive(Default, Clone)]
pub struct AuthState {
pub identity: RwSignal<Option<DelegationIdentity>>,
}

pub fn auth_state() -> AuthState {
expect_context()
}

/// Prevents hydration bugs if the value in store is used to conditionally show views
/// this is because the server will always get a `false` value and do rendering based on that
pub fn account_connected_reader() -> (ReadSignal<bool>, Effect<()>) {
let (read_account_connected, _, _) =
use_local_storage::<bool, StringCodec>(ACCOUNT_CONNECTED_STORE);
let (is_connected, set_is_connected) = create_signal(false);

(
is_connected,
create_effect(move |_| {
set_is_connected(read_account_connected());
}),
)
}
68 changes: 6 additions & 62 deletions src/state/auth.rs → src/state/auth/types.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
use std::num::ParseIntError;

use candid::Principal;
use ic_agent::identity::{DelegatedIdentity, Secp256k1Identity};
use k256::SecretKey;

use leptos::{create_effect, create_signal, expect_context, Effect, ReadSignal, RwSignal};
use leptos_use::storage::{use_local_storage, StringCodec};
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::consts::{ACCOUNT_CONNECTED_STORE, AUTH_URL};
use super::AuthError;

#[derive(Debug, Serialize, Clone)]
struct PrincipalId {
Expand Down Expand Up @@ -90,60 +85,9 @@ pub struct SessionResponse {
pub delegation_identity: DelegationIdentity,
}

#[derive(Error, Debug, Clone)]
pub enum AuthError {
#[error("Invalid Secret Key")]
InvalidSecretKey(#[from] k256::elliptic_curve::Error),
#[error("Invalid expiry")]
InvalidExpiry(#[from] ParseIntError),
#[error("reqwest error: {0}")]
Reqwest(String),
}

impl From<reqwest::Error> for AuthError {
fn from(e: reqwest::Error) -> Self {
AuthError::Reqwest(e.to_string())
}
}

#[derive(Default, Clone)]
pub struct AuthClient {
client: reqwest::Client,
}

impl AuthClient {
pub async fn generate_session(&self) -> Result<DelegatedIdentity, AuthError> {
let resp: SessionResponse = self
.client
.post(AUTH_URL.join("api/generate_session").unwrap())
.send()
.await?
.json()
.await?;
resp.delegation_identity.try_into()
}
}

#[derive(Default, Clone)]
pub struct AuthState {
pub identity: RwSignal<Option<DelegationIdentity>>,
}

pub fn auth_state() -> AuthState {
expect_context()
}

/// Prevents hydration bugs if the value in store is used to conditionally show views
/// this is because the server will always get a `false` value and do rendering based on that
pub fn account_connected_reader() -> (ReadSignal<bool>, Effect<()>) {
let (read_account_connected, _, _) =
use_local_storage::<bool, StringCodec>(ACCOUNT_CONNECTED_STORE);
let (is_connected, set_is_connected) = create_signal(false);

(
is_connected,
create_effect(move |_| {
set_is_connected(read_account_connected());
}),
)
#[derive(Serialize)]
pub struct UserDetails {
pub delegation_identity: DelegationIdentity,
pub user_canister_id: String,
pub user_name: String,
}
Loading

0 comments on commit 13dba67

Please sign in to comment.