diff --git a/services/identity/src/service.rs b/services/identity/src/service.rs index 0a5d0406c..0537c4b1e 100644 --- a/services/identity/src/service.rs +++ b/services/identity/src/service.rs @@ -1,91 +1,131 @@ use constant_time_eq::constant_time_eq; use futures_core::Stream; +use rand::{CryptoRng, Rng}; use rusoto_core::RusotoError; -use rusoto_dynamodb::GetItemError; +use rusoto_dynamodb::{GetItemError, PutItemError}; use std::pin::Pin; use tonic::{Request, Response, Status}; use tracing::{error, info, instrument}; use crate::database::DatabaseClient; +use crate::token::{AccessToken, AuthType}; use crate::{config::Config, database::Error}; pub use proto::identity_service_server::IdentityServiceServer; use proto::{ - identity_service_server::IdentityService, LoginRequest, LoginResponse, + identity_service_server::IdentityService, + login_response::Data::PakeLoginResponse, + login_response::Data::WalletLoginResponse, pake_login_response::Data::Token, + LoginRequest, LoginResponse, PakeLoginResponse as PakeLoginResponseStruct, RegistrationRequest, RegistrationResponse, VerifyUserTokenRequest, - VerifyUserTokenResponse, + VerifyUserTokenResponse, WalletLoginResponse as WalletLoginResponseStruct, }; mod proto { tonic::include_proto!("identity"); } #[derive(derive_more::Constructor)] pub struct MyIdentityService { config: Config, client: DatabaseClient, } #[tonic::async_trait] impl IdentityService for MyIdentityService { type RegisterUserStream = Pin< Box< dyn Stream> + Send + 'static, >, >; async fn register_user( &self, request: Request>, ) -> Result, Status> { println!("Got a registration request: {:?}", request); unimplemented!() } type LoginUserStream = Pin> + Send + 'static>>; async fn login_user( &self, request: Request>, ) -> Result, Status> { println!("Got a login request: {:?}", request); unimplemented!() } #[instrument(skip(self))] async fn verify_user_token( &self, request: Request, ) -> Result, Status> { info!("Received VerifyUserToken request: {:?}", request); let message = request.into_inner(); let token_valid = match self .client .get_access_token_data(message.user_id, message.device_id) .await { Ok(Some(access_token_data)) => constant_time_eq( access_token_data.access_token.as_bytes(), message.access_token.as_bytes(), ), Ok(None) => false, Err(Error::RusotoGet(RusotoError::Service( GetItemError::ResourceNotFound(_), ))) | Err(Error::RusotoGet(RusotoError::Credentials(_))) => { return Err(Status::failed_precondition("internal error")) } Err(Error::RusotoGet(_)) => { return Err(Status::unavailable("please retry")) } Err(e) => { error!("Encountered an unexpected error: {}", e); return Err(Status::failed_precondition("unexpected error")); } }; let response = Response::new(VerifyUserTokenResponse { token_valid }); info!("Sending VerifyUserToken response: {:?}", response); Ok(response) } } + +async fn put_token_helper( + client: DatabaseClient, + auth_type: AuthType, + user_id: String, + device_id: String, + rng: &mut (impl Rng + CryptoRng), +) -> Result { + let token = AccessToken::new(user_id, device_id, auth_type.clone(), rng); + match client.put_token(token.clone()).await { + Ok(_) => match auth_type { + AuthType::Wallet => Ok(LoginResponse { + data: Some(WalletLoginResponse(WalletLoginResponseStruct { + token: token.token, + })), + }), + AuthType::Password => Ok(LoginResponse { + data: Some(PakeLoginResponse(PakeLoginResponseStruct { + data: Some(Token(token.token)), + })), + }), + }, + Err(Error::RusotoPut(RusotoError::Service( + PutItemError::ResourceNotFound(_), + ))) + | Err(Error::RusotoPut(RusotoError::Credentials(_))) => { + Err(Status::failed_precondition("internal error")) + } + Err(Error::RusotoPut(_)) => Err(Status::unavailable("please retry")), + Err(e) => { + error!("Encountered an unexpected error: {}", e); + Err(Status::failed_precondition("unexpected error")) + } + } +} diff --git a/services/identity/src/token.rs b/services/identity/src/token.rs index 0b7a17e5c..f46662343 100644 --- a/services/identity/src/token.rs +++ b/services/identity/src/token.rs @@ -1,37 +1,38 @@ use chrono::{DateTime, Utc}; use rand::{ distributions::{Alphanumeric, DistString}, CryptoRng, Rng, }; +#[derive(Clone)] pub enum AuthType { Password, Wallet, } pub struct AccessTokenData { pub user_id: String, pub device_id: String, pub access_token: String, pub created: DateTime, pub auth_type: AuthType, pub valid: bool, } impl AccessTokenData { pub fn new( user_id: String, device_id: String, auth_type: AuthType, rng: &mut (impl Rng + CryptoRng), ) -> Self { AccessTokenData { user_id, device_id, access_token: Alphanumeric.sample_string(rng, 512), created: Utc::now(), auth_type, valid: true, } } }