Skip to content

Commit

Permalink
add login and sign up flow
Browse files Browse the repository at this point in the history
  • Loading branch information
RobDavenport committed Jan 30, 2024
1 parent 4f103a6 commit 99f8c2e
Show file tree
Hide file tree
Showing 9 changed files with 10,495 additions and 55 deletions.
112 changes: 89 additions & 23 deletions gamercade_app/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use eframe::egui::{self, TextEdit};
use eframe::egui::{self, TextEdit, Ui};

use crate::auth::AuthClient;

Expand All @@ -8,33 +8,99 @@ pub struct App {

username: String,
password: String,
email: String,

active_view: ActiveView,
}

#[derive(Default)]
enum ActiveView {
#[default]
Login,
SignUp,
Browsing,
}

impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
// TODO: Do this
ui.horizontal(|ui| {
ui.label("Username: ");
ui.text_edit_singleline(&mut self.username);
});

ui.horizontal(|ui| {
ui.label("Password: ");
let pw_entry = TextEdit::singleline(&mut self.password).password(true);
ui.add(pw_entry);
});

if ui.button("Login").clicked() {
self.auth_client.try_login(&self.username, &self.password);
self.username.clear();
self.password.clear();
}

if ui.button("Login as Guest").clicked() {
println!("TODO: Login as guest!")
}
// },
match self.active_view {
ActiveView::Login => self.draw_login(ctx, ui),
ActiveView::SignUp => self.draw_sign_up(ctx, ui),
ActiveView::Browsing => self.draw_browsing(ctx, ui),
};
});
}
}

impl App {
fn draw_login(&mut self, ctx: &egui::Context, ui: &mut Ui) {
ui.horizontal(|ui| {
ui.label("Username: ");
ui.text_edit_singleline(&mut self.username);
});

ui.horizontal(|ui| {
ui.label("Password: ");
let pw_entry = TextEdit::singleline(&mut self.password).password(true);
ui.add(pw_entry);
});

if ui.button("Sign Up").clicked() {
self.active_view = ActiveView::SignUp;
}

if ui.button("Login").clicked() {
self.auth_client.try_login(&self.username, &self.password);
//TODO: Lock entries while waiting
//TODO: Show an animation thing
self.clear_text();
}

if ui.button("Login as Guest").clicked() {
println!("TODO: Login as guest!")
}
}

fn draw_sign_up(&mut self, ctx: &egui::Context, ui: &mut Ui) {
ui.horizontal(|ui| {
ui.label("Username: ");
ui.text_edit_singleline(&mut self.username);
});

ui.horizontal(|ui| {
ui.label("Email Address: ");
let email = TextEdit::singleline(&mut self.email);
ui.add(email);
});

ui.horizontal(|ui| {
ui.label("Password: ");
let pw_entry = TextEdit::singleline(&mut self.password).password(true);
ui.add(pw_entry);
});

if ui.button("Register").clicked() {
self.auth_client
.try_register(&self.username, &self.email, &self.password);
// TODO: Lock the entries while waiting...
// TODO: Show an animation thing...
self.clear_text();
}

if ui.button("Cancel").clicked() {
self.clear_text();
self.active_view = ActiveView::Login;
}
}

fn draw_browsing(&mut self, ctx: &egui::Context, ui: &mut Ui) {
ui.label("Browsing");
}

fn clear_text(&mut self) {
self.username.clear();
self.email.clear();
self.password.clear();
}
}
114 changes: 85 additions & 29 deletions gamercade_app/src/auth/auth_client.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
use std::sync::Arc;

use gamercade_interface::auth::{auth_service_client::AuthServiceClient, LoginRequest};
use gamercade_interface::auth::{
auth_service_client::AuthServiceClient, login_request::Provider, LoginRequest,
RefreshTokenRequest, SignUpRequest,
};
use tokio::{
select,
sync::{
mpsc::{channel, Receiver, Sender},
RwLock,
},
};
use tonic::transport::Channel;

use crate::{auth::auth_state::AuthToken, ips::AUTH_IP};

use super::auth_state::AuthState;

pub struct AuthClient {
pub state: Arc<RwLock<AuthState>>,
sender: Sender<LoginRequest>,
sender: Sender<AuthClientRequest>,
}

pub enum AuthClientRequest {
Login(LoginRequest),
SignUp(SignUpRequest),
RefreshToken(RefreshTokenRequest),
}

impl Default for AuthClient {
Expand All @@ -31,16 +41,29 @@ impl Default for AuthClient {
impl AuthClient {
/// Asynchronously sends a login request to the Auth thread
pub fn try_login(&self, username: &str, password: &str) {
if let Err(_e) = self.sender.try_send(LoginRequest {
username: username.to_string(),
if let Err(_e) = self.sender.try_send(AuthClientRequest::Login(LoginRequest {
provider: Some(Provider::Username(username.to_string())),
password: password.to_string(),
}) {
})) {
panic!("Couldn't send login request over channel.");
};
}

pub fn try_register(&self, username: &str, email: &str, password: &str) {
if let Err(_e) = self
.sender
.try_send(AuthClientRequest::SignUp(SignUpRequest {
username: username.to_string(),
email: email.to_string(),
password: password.to_string(),
}))
{
panic!("Couldn't send login request over channel.");
};
}
}

fn spawn_task(auth_state: Arc<RwLock<AuthState>>) -> Sender<LoginRequest> {
fn spawn_task(auth_state: Arc<RwLock<AuthState>>) -> Sender<AuthClientRequest> {
let (auth_client_sender, rx) = channel(4);

tokio::spawn(async move { AuthTask::new(rx, auth_state).run().await });
Expand All @@ -49,13 +72,13 @@ fn spawn_task(auth_state: Arc<RwLock<AuthState>>) -> Sender<LoginRequest> {
}

struct AuthTask {
main_thread_receiver: Receiver<LoginRequest>,
main_thread_receiver: Receiver<AuthClientRequest>,
auth_state: Arc<RwLock<AuthState>>,
}

impl AuthTask {
fn new(
main_thread_receiver: Receiver<LoginRequest>,
main_thread_receiver: Receiver<AuthClientRequest>,
auth_state: Arc<RwLock<AuthState>>,
) -> Self {
Self {
Expand All @@ -69,32 +92,65 @@ impl AuthTask {

loop {
select! {
// Handle Login Requests
Some(login) = self.main_thread_receiver.recv() => {
match client.login(LoginRequest {
username: login.username,
password: login.password,
}).await {
Ok(response) => {
println!("Trying to login...");
let response = response.into_inner();
let mut write = self.auth_state.write().await;
*write = AuthState::TokensHeld(AuthToken {
access_token: response.access_token,
refresh_token: response.refresh_token,
expires_at: response.expires_at,
});
println!("Logged in successfully: {:?}", write);
},
Err(e) => {
println!("{e}");
}
// Handle Requests
Some(request) = self.main_thread_receiver.recv() => {
match request {
AuthClientRequest::Login(login) => self.handle_login(&mut client, login).await,
AuthClientRequest::SignUp(signup) => self.handle_sign_up(&mut client, signup).await,
AuthClientRequest::RefreshToken(refresh) => self.handle_refresh(&mut client, refresh).await,
}
}
}
}
}

async fn handle_login(
&mut self,
client: &mut AuthServiceClient<Channel>,
request: LoginRequest,
) {
println!("Trying to login...");
match client.login(request).await {
Ok(response) => {
let response = response.into_inner();
let mut write = self.auth_state.write().await;
*write = AuthState::TokensHeld(AuthToken {
access_token: response.access_token,
refresh_token: response.refresh_token,
expires_at: response.expires_at,
});
// TODO: Update the login page / move to browsing
println!("Logged in successfully: {:?}", write);
}
Err(e) => {
println!("{e}");
}
}
}

async fn handle_sign_up(
&mut self,
client: &mut AuthServiceClient<Channel>,
request: SignUpRequest,
) {
println!("Trying to sign up...");
match client.sign_up(request).await {
Ok(_) => {
// TODO: Update the login page / move to Login
println!("Signed up successfully.");
}
Err(e) => {
// TODO:
// Handle Refresh Requests
println!("{e}");
}
}
}

async fn handle_refresh(
&mut self,
client: &mut AuthServiceClient<Channel>,
request: RefreshTokenRequest,
) {
todo!()
}
}
3 changes: 3 additions & 0 deletions gamercade_interface/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

Proto Definitions for gRPC between client and back end.

Banned passwords list from:
https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-Credentials/10-million-password-list-top-10000.txt

## License

Licensed under either of
Expand Down
25 changes: 23 additions & 2 deletions gamercade_interface/proto/auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@ import "common.proto";
package auth;

service AuthService {
rpc SignUp(SignUpRequest) returns (common.Empty);
rpc Login(LoginRequest) returns (LoginResponse);
rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
// TODO: handle refresh tokens?
}

message LoginRequest {
message SignUpRequest {
string username = 1;
string password = 2;
string email = 2;
string password = 3;
}

message LoginRequest {
string password = 1;
oneof provider {
string username = 2;
string email = 3;
};
}

message LoginResponse {
Expand All @@ -20,3 +31,13 @@ message LoginResponse {
uint64 expires_at = 3;
}

message RefreshTokenRequest {
string refresh_token = 1;
}

message RefreshTokenResponse {
string access_token = 1;
string refresh_token = 2;
uint64 expires_at = 3;
}

2 changes: 2 additions & 0 deletions gamercade_interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ pub use output::*;

mod network_session;
pub use network_session::*;

pub mod security;
Loading

0 comments on commit 99f8c2e

Please sign in to comment.