diff --git a/src/token_management.rs b/src/token_management.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fc37f9354bd9d82e5c61103b9c1f51b7194060c2
--- /dev/null
+++ b/src/token_management.rs
@@ -0,0 +1,71 @@
+use chrono::{
+    prelude::{SecondsFormat, Utc},
+    DateTime,
+};
+use rand::Rng;
+use serenity::model::user::User;
+use std::str;
+
+lazy_static! {
+    static ref key: [u8; 32] = rand::thread_rng().gen::<[u8; 32]>();
+}
+
+fn encrypt(plaintext: &str) -> &str {
+    return plaintext;
+}
+fn decrypt(ciphertext: &str) -> &str {
+    return ciphertext;
+}
+
+pub fn generate_token<'a>(discord_user: &User, username: &str) -> String {
+    // if username doesn't exist : throw error
+    let timestamp = Utc::now().to_rfc3339();
+    let payload = format!(
+        "{},{},{}",
+        timestamp,
+        discord_user.id.0.to_string(),
+        username
+    );
+    info!("Token generated for {}: {}", discord_user.name, &payload);
+    encrypt(&payload).to_string()
+}
+
+#[derive(Debug)]
+pub enum TokenError {
+    DiscordIdMismatch,
+    TokenExpired,
+}
+impl std::fmt::Display for TokenError {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "{:?}", self)
+    }
+}
+
+pub fn parse_token(discord_user: &User, token: &str) -> Result<String, TokenError> {
+    let token_components: Vec<_> = decrypt(token).splitn(3, ',').collect();
+    info!(
+        "Verification attempt from '{}'(uid: {}) for account '{}' with token from {}",
+        discord_user.name, token_components[1], token_components[2], token_components[0]
+    );
+    let token_timestamp =
+        DateTime::parse_from_rfc3339(token_components[0]).expect("Invalid date format");
+    let token_discord_user = token_components[1];
+    let token_username = token_components[2];
+    if token_discord_user != discord_user.id.0.to_string() {
+        warn!("... attempt failed : DiscordID mismatch");
+        return Err(TokenError::DiscordIdMismatch);
+    }
+    let time_delta_seconds = Utc::now().timestamp() - token_timestamp.timestamp();
+    if time_delta_seconds > 5 * 60 {
+        warn!(
+            "... attempt failed : token expired ({} seconds old)",
+            time_delta_seconds
+        );
+        return Err(TokenError::TokenExpired);
+    }
+    info!(
+        "... verification successful (token {} seconds old)",
+        time_delta_seconds
+    );
+    return Ok(token_username.to_owned());
+}
diff --git a/src/user_management.rs b/src/user_management.rs
index 830f9640b274a2746e9ccb2288f3c847a6b56d8d..0ba3f2e114697638782e2752ef9f6feb1bb4ef3c 100644
--- a/src/user_management.rs
+++ b/src/user_management.rs
@@ -6,6 +6,7 @@ use serenity::{
 };
 
 use crate::config::CONFIG;
+use crate::token_management::*;
 
 macro_rules! e {
     ($error: literal, $x:expr) => {
@@ -59,48 +60,63 @@ impl Commands {
             );
             return;
         }
-        // token stuff
+        e!(
+            "Error sending message: {:?}",
+            // TODO convert to email
+            msg.channel_id
+                .say(&ctx.http, generate_token(&msg.author, name))
+        );
         e!("Error deleting register message: {:?}", msg.delete(ctx));
     }
     pub fn verify(ctx: Context, msg: Message, content: &str) {
         let token = content;
-        // if token is valid
-        e!(
-            "Unable to get member: {:?}",
-            serenity::model::id::GuildId(CONFIG.server_id)
-                .member(ctx.http.clone(), msg.author.id)
-                .map(|mut member| {
-                    e!(
-                        "Unable to remove role: {:?}",
-                        member.remove_role(&ctx.http, CONFIG.unregistered_member_role)
-                    );
-                    e!(
-                        "Unable to edit nickname: {:?}",
-                        member
-                            .edit(&ctx.http, |m| {
-                                let mut rng = rand::thread_rng();
-                                m.nickname(format!(
-                                    "{}, {}",
-                                    name,
-                                    [
-                                        "The Big Cheese",
-                                        "The One and Only",
-                                        "The Exalted One",
-                                        "not to be trusted",
-                                        "The Scoundrel",
-                                        "A big fish in a small pond",
-                                    ][rng.gen_range(0, 5)]
-                                ));
-                                m
-                            })
-                            .map(|()| {
-                                e!(
-                                    "Unable to add role: {:?}",
-                                    member.add_role(&ctx.http, CONFIG.registered_member_role)
-                                );
-                            })
-                    );
-                })
-        );
+        match parse_token(&msg.author, content) {
+            Ok(name) => {
+                e!(
+                    "Unable to get member: {:?}",
+                    serenity::model::id::GuildId(CONFIG.server_id)
+                        .member(ctx.http.clone(), msg.author.id)
+                        .map(|mut member| {
+                            e!(
+                                "Unable to remove role: {:?}",
+                                member.remove_role(&ctx.http, CONFIG.unregistered_member_role)
+                            );
+                            e!(
+                                "Unable to edit nickname: {:?}",
+                                member.edit(&ctx.http, |m| {
+                                    let mut rng = rand::thread_rng();
+                                    m.nickname(format!(
+                                        "{}, {}",
+                                        name,
+                                        [
+                                            "The Big Cheese",
+                                            "The One and Only",
+                                            "The Exalted One",
+                                            "not to be trusted",
+                                            "The Scoundrel",
+                                            "A big fish in a small pond",
+                                        ][rng.gen_range(0, 5)]
+                                    ));
+                                    m
+                                })
+                            );
+                            let new_msg = msg
+                                .channel_id
+                                .say(&ctx.http, "Verification succesful")
+                                .expect("Error sending message");
+                            e!(
+                                "Error deleting register message: {:?}",
+                                new_msg.delete(&ctx)
+                            );
+                        })
+                );
+            }
+            Err(reason) => e!(
+                "Error sending message: {:?}",
+                msg.channel_id
+                    .say(&ctx.http, format!("Verification error: {:?}", reason))
+            ),
+        }
+        e!("Error deleting register message: {:?}", msg.delete(&ctx));
     }
 }