diff --git a/Cargo.toml b/Cargo.toml index aef435f..2b9962c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,4 +62,6 @@ random-string = "1.1" lettre = { version = "0.11", features = ["tokio1", "tokio1-native-tls"] } chrono = { version = "0.4.41", features = ["serde"] } tracing-subscriber = "0.3.19" +rand = "0.9.1" + diff --git a/migrations/2025-07-15-002434_increase_device_name_length/down.sql b/migrations/2025-07-15-002434_increase_device_name_length/down.sql new file mode 100644 index 0000000..4fe6628 --- /dev/null +++ b/migrations/2025-07-15-002434_increase_device_name_length/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE refresh_tokens ALTER COLUMN device_name TYPE varchar(16); \ No newline at end of file diff --git a/migrations/2025-07-15-002434_increase_device_name_length/up.sql b/migrations/2025-07-15-002434_increase_device_name_length/up.sql new file mode 100644 index 0000000..9d44298 --- /dev/null +++ b/migrations/2025-07-15-002434_increase_device_name_length/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE refresh_tokens ALTER COLUMN device_name TYPE varchar(64); \ No newline at end of file diff --git a/src/api/v1/auth/login.rs b/src/api/v1/auth/login.rs index d5cba95..61cb6a0 100644 --- a/src/api/v1/auth/login.rs +++ b/src/api/v1/auth/login.rs @@ -21,7 +21,7 @@ use crate::{ schema::*, utils::{ PASSWORD_REGEX, generate_token, new_refresh_token_cookie, - user_uuid_from_identifier, + user_uuid_from_identifier, generate_device_name }, }; @@ -29,7 +29,6 @@ use crate::{ pub struct LoginInformation { username: String, password: String, - device_name: String, } pub async fn response( @@ -72,12 +71,14 @@ pub async fn response( use refresh_tokens::dsl as rdsl; + let device_name = generate_device_name(); + insert_into(refresh_tokens::table) .values(( rdsl::token.eq(&refresh_token), rdsl::uuid.eq(uuid), rdsl::created_at.eq(current_time), - rdsl::device_name.eq(&login_information.device_name), + rdsl::device_name.eq(&device_name), )) .execute(&mut conn) .await?; @@ -94,7 +95,7 @@ pub async fn response( .execute(&mut conn) .await?; - let mut response = (StatusCode::OK, Json(Response { access_token })).into_response(); + let mut response = (StatusCode::OK, Json(Response { access_token, device_name })).into_response(); response.headers_mut().append( "Set-Cookie", diff --git a/src/api/v1/auth/mod.rs b/src/api/v1/auth/mod.rs index 899d6d2..c579899 100644 --- a/src/api/v1/auth/mod.rs +++ b/src/api/v1/auth/mod.rs @@ -27,6 +27,7 @@ mod verify_email; #[derive(Serialize)] pub struct Response { access_token: String, + device_name: String, } diff --git a/src/api/v1/auth/refresh.rs b/src/api/v1/auth/refresh.rs index e9709ed..ee4f7ae 100644 --- a/src/api/v1/auth/refresh.rs +++ b/src/api/v1/auth/refresh.rs @@ -19,8 +19,7 @@ use crate::{ schema::{ access_tokens::{self, dsl}, refresh_tokens::{self, dsl as rdsl}, - }, - utils::{generate_token, new_refresh_token_cookie}, + }, utils::{generate_token, new_refresh_token_cookie} }; pub async fn post( @@ -69,6 +68,7 @@ pub async fn post( } let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64; + let mut device_name: String = String::new(); if lifetime > 1987200 { let new_refresh_token = generate_token::<32>()?; @@ -79,11 +79,13 @@ pub async fn post( rdsl::token.eq(&new_refresh_token), rdsl::created_at.eq(current_time), )) - .execute(&mut conn) + .returning(rdsl::device_name) + .get_result::(&mut conn) .await { - Ok(_) => { + Ok(existing_device_name) => { refresh_token = new_refresh_token; + device_name = existing_device_name; } Err(error) => { error!("{error}"); @@ -102,7 +104,7 @@ pub async fn post( .execute(&mut conn) .await?; - let mut response = (StatusCode::OK, Json(Response { access_token })).into_response(); + let mut response = (StatusCode::OK, Json(Response { access_token, device_name })).into_response(); // TODO: Dont set this when refresh token is unchanged response.headers_mut().append( diff --git a/src/api/v1/auth/register.rs b/src/api/v1/auth/register.rs index c190821..f2520bf 100644 --- a/src/api/v1/auth/register.rs +++ b/src/api/v1/auth/register.rs @@ -30,7 +30,7 @@ use crate::{ }, utils::{ EMAIL_REGEX, PASSWORD_REGEX, USERNAME_REGEX, generate_token, - new_refresh_token_cookie, + new_refresh_token_cookie, generate_device_name }, }; @@ -39,7 +39,6 @@ pub struct AccountInformation { identifier: String, email: String, password: String, - device_name: String, } #[derive(Serialize)] @@ -137,12 +136,14 @@ pub async fn post( let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64; + let device_name = generate_device_name(); + insert_into(refresh_tokens::table) - .values(( - rdsl::token.eq(&refresh_token), - rdsl::uuid.eq(uuid), + .values(( + rdsl::token.eq(&refresh_token), + rdsl::uuid.eq(uuid), rdsl::created_at.eq(current_time), - rdsl::device_name.eq(&account_information.device_name), + rdsl::device_name.eq(&device_name), )) .execute(&mut conn) .await?; @@ -161,7 +162,7 @@ pub async fn post( Member::new(&app_state, uuid, initial_guild).await?; } - let mut response = (StatusCode::OK, Json(Response {access_token})).into_response(); + let mut response = (StatusCode::OK, Json(Response {access_token, device_name})).into_response(); response.headers_mut().append( "Set-Cookie", diff --git a/src/main.rs b/src/main.rs index 8e6effc..ffbfa4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,6 @@ use objects::MailClient; use socketioxide::SocketIo; use std::{sync::Arc, time::SystemTime}; use tower_http::cors::{AllowOrigin, CorsLayer}; -mod config; use config::{Config, ConfigBuilder}; use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations}; @@ -17,6 +16,8 @@ pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); type Conn = deadpool::managed::Object>; +mod config; +mod wordlist; mod api; pub mod error; pub mod objects; diff --git a/src/schema.rs b/src/schema.rs index 2693b02..4095dcd 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -103,7 +103,7 @@ diesel::table! { token -> Varchar, uuid -> Uuid, created_at -> Int8, - #[max_length = 16] + #[max_length = 64] device_name -> Varchar, } } diff --git a/src/utils.rs b/src/utils.rs index 0f986a2..ac8e343 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,5 @@ use std::sync::LazyLock; +use rand::{seq::IndexedRandom}; use axum::body::Bytes; use axum_extra::extract::cookie::{Cookie, SameSite}; @@ -19,6 +20,7 @@ use crate::{ error::Error, objects::{HasIsAbove, HasUuid}, schema::users, + wordlist::{ADJECTIVES, ANIMALS} }; pub static EMAIL_REGEX: LazyLock = LazyLock::new(|| { @@ -207,3 +209,12 @@ impl AppState { .await } } + +pub fn generate_device_name() -> String { + let mut rng = rand::rng(); + + let adjective = ADJECTIVES.choose(&mut rng).unwrap(); + let animal = ANIMALS.choose(&mut rng).unwrap(); + + return [*adjective, *animal].join(" ") +} diff --git a/src/wordlist.rs b/src/wordlist.rs new file mode 100644 index 0000000..0c17723 --- /dev/null +++ b/src/wordlist.rs @@ -0,0 +1,993 @@ +pub const ANIMALS: [&'static str; 223] = [ + "Aardvark", + "Albatross", + "Alligator", + "Alpaca", + "Ant", + "Anteater", + "Antelope", + "Ape", + "Armadillo", + "Donkey", + "Baboon", + "Badger", + "Barracuda", + "Bat", + "Bear", + "Beaver", + "Bee", + "Bison", + "Boar", + "Buffalo", + "Butterfly", + "Camel", + "Capybara", + "Caribou", + "Cassowary", + "Cat", + "Caterpillar", + "Cattle", + "Chamois", + "Cheetah", + "Chicken", + "Chimpanzee", + "Chinchilla", + "Chough", + "Clam", + "Cobra", + "Cockroach", + "Cod", + "Cormorant", + "Coyote", + "Crab", + "Crane", + "Crocodile", + "Crow", + "Curlew", + "Deer", + "Dinosaur", + "Dog", + "Dogfish", + "Dolphin", + "Dotterel", + "Dove", + "Dragonfly", + "Duck", + "Dugong", + "Dunlin", + "Eagle", + "Echidna", + "Eel", + "Eland", + "Elephant", + "Elk", + "Emu", + "Falcon", + "Ferret", + "Finch", + "Fish", + "Flamingo", + "Fly", + "Fox", + "Frog", + "Gaur", + "Gazelle", + "Gerbil", + "Giraffe", + "Gnat", + "Gnu", + "Goat", + "Goldfinch", + "Goldfish", + "Goose", + "Gorilla", + "Goshawk", + "Grasshopper", + "Grouse", + "Guanaco", + "Gull", + "Hamster", + "Hare", + "Hawk", + "Hedgehog", + "Heron", + "Herring", + "Hippopotamus", + "Hornet", + "Horse", + "Hummingbird", + "Hyena", + "Ibex", + "Ibis", + "Jackal", + "Jaguar", + "Jay", + "Jellyfish", + "Kangaroo", + "Kingfisher", + "Koala", + "Kookabura", + "Kouprey", + "Kudu", + "Lapwing", + "Lark", + "Lemur", + "Leopard", + "Lion", + "Llama", + "Lobster", + "Locust", + "Loris", + "Louse", + "Lyrebird", + "Magpie", + "Mallard", + "Manatee", + "Mandrill", + "Mantis", + "Marten", + "Meerkat", + "Mink", + "Mole", + "Mongoose", + "Monkey", + "Moose", + "Mosquito", + "Mouse", + "Mule", + "Narwhal", + "Newt", + "Nightingale", + "Octopus", + "Okapi", + "Opossum", + "Oryx", + "Ostrich", + "Otter", + "Owl", + "Oyster", + "Panther", + "Parrot", + "Partridge", + "Peafowl", + "Pelican", + "Penguin", + "Pheasant", + "Pig", + "Pigeon", + "Pony", + "Porcupine", + "Porpoise", + "Quail", + "Quelea", + "Quetzal", + "Rabbit", + "Raccoon", + "Rail", + "Ram", + "Rat", + "Raven", + "Red Deer", + "Red Panda", + "Reindeer", + "Rhinoceros", + "Rook", + "Salamander", + "Salmon", + "Sand Dollar", + "Sandpiper", + "Sardine", + "Scorpion", + "Seahorse", + "Seal", + "Shark", + "Sheep", + "Shrew", + "Skunk", + "Snail", + "Snake", + "Sparrow", + "Spider", + "Spoonbill", + "Squid", + "Squirrel", + "Starling", + "Stingray", + "Stinkbug", + "Stork", + "Swallow", + "Swan", + "Tapir", + "Tarsier", + "Termite", + "Tiger", + "Toad", + "Trout", + "Turkey", + "Turtle", + "Viper", + "Vulture", + "Wallaby", + "Walrus", + "Wasp", + "Weasel", + "Whale", + "Wildcat", + "Wolf", + "Wolverine", + "Wombat", + "Woodcock", + "Woodpecker", + "Worm", + "Wren", + "Yak", + "Zebra", +]; + +pub const ADJECTIVES: [&'static str; 765] = [ + "Other", + "Such", + "First", + "Many", + "New", + "More", + "Same", + "Own", + "Good", + "Different", + "Great", + "Long", + "High", + "Social", + "Little", + "Much", + "Important", + "Small", + "Most", + "Large", + "Old", + "Few", + "General", + "Second", + "Public", + "Last", + "Several", + "Early", + "Certain", + "Economic", + "Least", + "Common", + "Present", + "Next", + "Local", + "Best", + "Particular", + "Young", + "Various", + "Necessary", + "Whole", + "Only", + "True", + "Able", + "Major", + "Full", + "Low", + "Available", + "Real", + "Similar", + "Total", + "Special", + "Less", + "Short", + "Specific", + "Single", + "Self", + "National", + "Individual", + "Clear", + "Personal", + "Higher", + "Better", + "Third", + "Natural", + "Greater", + "Open", + "Difficult", + "Current", + "Further", + "Main", + "Physical", + "Foreign", + "Lower", + "Strong", + "Private", + "Likely", + "International", + "Significant", + "Late", + "Basic", + "Hard", + "Modern", + "Simple", + "Normal", + "Sure", + "Central", + "Original", + "Effective", + "Following", + "Direct", + "Final", + "Cultural", + "Big", + "Recent", + "Complete", + "Financial", + "Positive", + "Primary", + "Appropriate", + "Legal", + "European", + "Equal", + "Larger", + "Average", + "Historical", + "Critical", + "Wide", + "Traditional", + "Additional", + "Active", + "Complex", + "Former", + "Independent", + "Entire", + "Actual", + "Close", + "Constant", + "Previous", + "Easy", + "Serious", + "Potential", + "Fine", + "Industrial", + "Subject", + "Future", + "Internal", + "Initial", + "Well", + "Essential", + "Dark", + "Popular", + "Successful", + "Standard", + "Year", + "Past", + "Ready", + "Professional", + "Wrong", + "Very", + "Proper", + "Separate", + "Heavy", + "Civil", + "Responsible", + "Considerable", + "Light", + "Cold", + "Above", + "Older", + "Practical", + "External", + "Sufficient", + "Interesting", + "Upper", + "Scientific", + "Key", + "Annual", + "Limited", + "Smaller", + "Southern", + "Earlier", + "Commercial", + "Powerful", + "Later", + "Like", + "Clinical", + "Ancient", + "Educational", + "Typical", + "Technical", + "Environmental", + "Formal", + "Aware", + "Beautiful", + "Variable", + "Obvious", + "Secondary", + "Enough", + "Urban", + "Regular", + "Relevant", + "Greatest", + "Spiritual", + "Time", + "Double", + "Happy", + "Term", + "Multiple", + "Dependent", + "Correct", + "Northern", + "Middle", + "Rural", + "Official", + "Fundamental", + "Numerous", + "Overall", + "Usual", + "Native", + "Regional", + "Highest", + "North", + "Agricultural", + "Literary", + "Broad", + "Perfect", + "Experimental", + "Fourth", + "Global", + "Ordinary", + "Related", + "Apparent", + "Daily", + "Principal", + "Contemporary", + "Severe", + "Reasonable", + "Subsequent", + "Worth", + "Longer", + "Emotional", + "Intellectual", + "Unique", + "Pure", + "Familiar", + "American", + "Solid", + "Brief", + "Famous", + "Fresh", + "Day", + "Corresponding", + "Characteristic", + "Maximum", + "Detailed", + "Outside", + "Theoretical", + "Fair", + "Opposite", + "Capable", + "Visual", + "Interested", + "Joint", + "Adequate", + "Based", + "Substantial", + "Unable", + "Structural", + "Soft", + "False", + "Largest", + "Inner", + "Mean", + "Extensive", + "Excellent", + "Rapid", + "Absolute", + "Consistent", + "Continuous", + "Administrative", + "Strange", + "Willing", + "Alternative", + "Slow", + "Distinct", + "Safe", + "Permanent", + "Front", + "Corporate", + "Academic", + "Thin", + "Nineteenth", + "Universal", + "Functional", + "Unknown", + "Careful", + "Narrow", + "Evident", + "Sound", + "Classical", + "Minor", + "Weak", + "Suitable", + "Chief", + "Extreme", + "Yellow", + "Warm", + "Mixed", + "Flat", + "Huge", + "Vast", + "Stable", + "Valuable", + "Rare", + "Visible", + "Sensitive", + "Mechanical", + "State", + "Radical", + "Extra", + "Superior", + "Conventional", + "Thick", + "Dominant", + "Post", + "Collective", + "Younger", + "Efficient", + "Linear", + "Organic", + "Oral", + "Century", + "Creative", + "Vertical", + "Dynamic", + "Empty", + "Minimum", + "Cognitive", + "Logical", + "Afraid", + "Equivalent", + "Quick", + "Near", + "Concrete", + "Mass", + "Acute", + "Sharp", + "Easier", + "Quiet", + "Adult", + "Accurate", + "Ideal", + "Partial", + "Bright", + "Identical", + "Conservative", + "Magnetic", + "Frequent", + "Electronic", + "Fixed", + "Square", + "Cross", + "Clean", + "Back", + "Organizational", + "Constitutional", + "Genetic", + "Ultimate", + "Secret", + "Vital", + "Dramatic", + "Objective", + "Round", + "Alive", + "Straight", + "Unusual", + "Rational", + "Electric", + "Mutual", + "Class", + "Competitive", + "Revolutionary", + "Statistical", + "Random", + "Musical", + "Crucial", + "Racial", + "Sudden", + "Acid", + "Content", + "Temporary", + "Line", + "Remarkable", + "Exact", + "Valid", + "Helpful", + "Nice", + "Comprehensive", + "United", + "Level", + "Fifth", + "Nervous", + "Expensive", + "Prominent", + "Healthy", + "Liquid", + "Institutional", + "Silent", + "Sweet", + "Strategic", + "Molecular", + "Comparative", + "Called", + "Electrical", + "Raw", + "Acceptable", + "Scale", + "Violent", + "All", + "Desirable", + "Tall", + "Steady", + "Wonderful", + "Sub", + "Distant", + "Progressive", + "Enormous", + "Horizontal", + "And", + "Intense", + "Smooth", + "Applicable", + "Over", + "Animal", + "Abstract", + "Wise", + "Worst", + "Gold", + "Precise", + "Legislative", + "Remote", + "Technological", + "Outer", + "Uniform", + "Slight", + "Attractive", + "Evil", + "Tiny", + "Royal", + "Angry", + "Advanced", + "Friendly", + "Dear", + "Busy", + "Spatial", + "Rough", + "Primitive", + "Judicial", + "Systematic", + "Lateral", + "Sorry", + "Plain", + "Off", + "Comfortable", + "Definite", + "Massive", + "Firm", + "Widespread", + "Prior", + "Twentieth", + "Mathematical", + "Verbal", + "Marginal", + "Excessive", + "Stronger", + "Gross", + "World", + "Productive", + "Wider", + "Glad", + "Linguistic", + "Patient", + "Symbolic", + "Earliest", + "Plastic", + "Type", + "Prime", + "Eighteenth", + "Blind", + "Neutral", + "Guilty", + "Hand", + "Extraordinary", + "Metal", + "Surprising", + "Fellow", + "York", + "Grand", + "Thermal", + "Artificial", + "Five", + "Lowest", + "Genuine", + "Dimensional", + "Optical", + "Unlikely", + "Developmental", + "Reliable", + "Executive", + "Comparable", + "Satisfactory", + "Golden", + "Diverse", + "Preliminary", + "Wooden", + "Noble", + "Part", + "Striking", + "Cool", + "Classic", + "Elderly", + "Four", + "Temporal", + "Indirect", + "Romantic", + "Intermediate", + "Differential", + "Passive", + "Life", + "Voluntary", + "Out", + "Adjacent", + "Behavioral", + "Exclusive", + "Closed", + "Inherent", + "Inevitable", + "Complicated", + "Quantitative", + "Respective", + "Artistic", + "Probable", + "Anxious", + "Informal", + "Strict", + "Fiscal", + "Ideological", + "Profound", + "Extended", + "Eternal", + "Known", + "Infinite", + "Proud", + "Honest", + "Peculiar", + "Absent", + "Pleasant", + "Optimal", + "Renal", + "Static", + "Outstanding", + "Presidential", + "Digital", + "Integrated", + "Legitimate", + "Curious", + "Aggressive", + "Deeper", + "Elementary", + "History", + "Surgical", + "Occasional", + "Flexible", + "Convenient", + "Solar", + "Atomic", + "Isolated", + "Latest", + "Sad", + "Conceptual", + "Underlying", + "Everyday", + "Cost", + "Intensive", + "Odd", + "Subjective", + "Mid", + "Worthy", + "Pale", + "Meaningful", + "Therapeutic", + "Making", + "Circular", + "Realistic", + "Multi", + "Child", + "Sophisticated", + "Down", + "Leading", + "Intelligent", + "Governmental", + "Numerical", + "Minimal", + "Diagnostic", + "Indigenous", + "Aesthetic", + "Distinctive", + "Operational", + "Sole", + "Material", + "Fast", + "Bitter", + "Broader", + "Brilliant", + "Peripheral", + "Rigid", + "Automatic", + "Lesser", + "Routine", + "Favorable", + "Cooperative", + "Cardiac", + "Arbitrary", + "Loose", + "Favorite", + "Subtle", + "Uncertain", + "Hostile", + "Monthly", + "Naval", + "Physiological", + "Historic", + "Developed", + "Skilled", + "Anterior", + "Pro", + "Gentle", + "Loud", + "Pulmonary", + "Innocent", + "Provincial", + "Mild", + "Page", + "Specialized", + "Bare", + "Excess", + "Inter", + "Shaped", + "Theological", + "Sensory", + "The", + "Stress", + "Novel", + "Working", + "Shorter", + "Secular", + "Geographical", + "Intimate", + "Liable", + "Selective", + "Influential", + "Modest", + "Successive", + "Continued", + "Water", + "Expert", + "Municipal", + "Marine", + "Thirty", + "Adverse", + "Wacky", + "Closer", + "Virtual", + "Peaceful", + "Mobile", + "Sixth", + "Immune", + "Coastal", + "Representative", + "Lead", + "Forward", + "Faithful", + "Crystal", + "Protective", + "Elaborate", + "Tremendous", + "Welcoming", + "Abnormal", + "Grateful", + "Proportional", + "Dual", + "Operative", + "Precious", + "Sympathetic", + "Accessible", + "Lovely", + "Spinal", + "Even", + "Marked", + "Observed", + "Point", + "Mature", + "Competent", + "Residential", + "Impressive", + "Unexpected", + "Nearby", + "Unnecessary", + "Generous", + "Cerebral", + "Unpublished", + "Delicate", + "Analytical", + "Tropical", + "Statutory", + "Cell", + "Weekly", + "End", + "Online", + "Beneficial", + "Aged", + "Tough", + "Eager", + "Ongoing", + "Silver", + "Persistent", + "Calm", + "Nearest", + "Hidden", + "Magic", + "Pretty", + "Wealthy", + "Exciting", + "Decisive", + "Confident", + "Invisible", + "Notable", + "Medium", + "Manual", + "Select", + "Thorough", + "Causal", + "Giant", + "Bigger", + "Pink", + "Improved", + "Immense", + "Hour", + "Intact", + "Grade", + "Dense", + "Hungry", + "Biggest", + "Abundant", + "Handsome", + "Retail", + "Insufficient", + "Irregular", + "Intrinsic", + "Residual", + "Follow", + "Fluid", + "Mysterious", + "Descriptive", + "Elastic", + "Destructive", + "Architectural", + "Synthetic", + "Continental", + "Evolutionary", + "Lucky", + "Bold", + "Funny", + "Peak", + "Smallest", + "Reluctant", + "Suspicious", + "Smart", + "Mighty", + "Brave", + "Humble", + "Vocal", + "Obscure", + "Innovative", +]; \ No newline at end of file