diff --git a/client/src/components/login.jsx b/client/src/components/login.jsx
index 2d76d3ad..34737640 100644
--- a/client/src/components/login.jsx
+++ b/client/src/components/login.jsx
@@ -3,38 +3,22 @@ const preact = require('preact');
const { Component } = require('preact')
const { connect } = require('preact-redux');
-const SERVER = process.env.NODE_ENV === 'production' ? '/' : 'http://localhost:40000';
-
-function postData(url = '/', data = {}) {
- // Default options are marked with *
- return fetch(url, {
- method: "POST", // *GET, POST, PUT, DELETE, etc.
- // mode: "no-cors", // no-cors, cors, *same-origin
- cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
- credentials: "include", // include, same-origin, *omit
- headers: {
- 'Accept': 'application/json',
- 'content-type': 'application/json'
- },
- redirect: "error", // manual, *follow, error
- // referrer: "", // no-referrer, *client
- body: JSON.stringify(data), // body data type must match "Content-Type" header
- })
- .then(response => response.json()); // parses response to JSON
-}
+const { postData } = require('../utils');
const addState = connect(
- null,
- (dispatch) => {
+ (state) => {
+ const {
+ ws
+ } = state;
function submitLogin(name, password) {
- postData(`${SERVER}/login`, { name, password })
- .then(data => console.log(JSON.stringify(data)))
+ postData('/login', { name, password })
+ .then(data => ws.connect())
.catch(error => console.error(error));
}
function submitRegister(name, password, code) {
- postData(`${SERVER}/register`, { name, password, code })
- .then(data => console.log(JSON.stringify(data)))
+ postData('/register', { name, password, code })
+ .then(data => ws.connect())
.catch(error => console.error(error));
}
diff --git a/client/src/components/nav.jsx b/client/src/components/nav.jsx
index 931fa328..57d1665e 100644
--- a/client/src/components/nav.jsx
+++ b/client/src/components/nav.jsx
@@ -1,8 +1,9 @@
const { connect } = require('preact-redux');
const preact = require('preact');
const { Fragment } = require('preact');
-const actions = require('../actions');
+const { postData } = require('../utils');
+const actions = require('../actions');
const { saw } = require('./shapes');
const testGame = process.env.NODE_ENV === 'development' && require('./../test.game');
@@ -38,6 +39,10 @@ const addState = connect(
return ws.sendInstanceList();
}
+ function logout() {
+ postData('/logout').then(() => window.location.reload(true));
+ }
+
return {
account,
instances,
@@ -47,6 +52,7 @@ const addState = connect(
sendInstanceState,
sendAccountInstances,
sendInstanceList,
+ logout,
};
},
function receiveDispatch(dispatch) {
@@ -97,6 +103,7 @@ function Nav(args) {
sendInstanceState,
sendAccountInstances,
sendInstanceList,
+ logout,
setTestGame,
setTestInstance,
@@ -133,6 +140,7 @@ function Nav(args) {
Hax
+
)
: null;
diff --git a/client/src/utils.jsx b/client/src/utils.jsx
index 3b506704..5c328a34 100644
--- a/client/src/utils.jsx
+++ b/client/src/utils.jsx
@@ -345,6 +345,24 @@ const TARGET_COLOURS = {
BROWN: '#583108',
};
+const SERVER = process.env.NODE_ENV === 'production' ? '/' : 'http://localhost:40000';
+function postData(url = '/', data = {}) {
+ // Default options are marked with *
+ return fetch(`${SERVER}${url}`, {
+ method: "POST", // *GET, POST, PUT, DELETE, etc.
+ // mode: "no-cors", // no-cors, cors, *same-origin
+ cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
+ credentials: "include", // include, same-origin, *omit
+ headers: {
+ 'Accept': 'application/json',
+ 'content-type': 'application/json'
+ },
+ redirect: "error", // manual, *follow, error
+ // referrer: "", // no-referrer, *client
+ body: JSON.stringify(data), // body data type must match "Content-Type" header
+ })
+}
+
module.exports = {
stringSort,
convertItem,
@@ -352,6 +370,8 @@ module.exports = {
eventClasses,
getCombatSequence,
getCombatText,
+ postData,
+ SERVER,
NULL_UUID,
STATS,
COLOURS,
diff --git a/ops/migrations/20180913000513_create_accounts.js b/ops/migrations/20180913000513_create_accounts.js
index 675bfc7c..d0699f89 100755
--- a/ops/migrations/20180913000513_create_accounts.js
+++ b/ops/migrations/20180913000513_create_accounts.js
@@ -4,7 +4,7 @@ exports.up = async knex => {
table.timestamps(true, true);
table.string('name', 42).notNullable().unique();
table.string('password').notNullable();
- table.string('token', 64);
+ table.string('token', 64).notNullable();
table.timestamp('token_expiry');
table.index('name');
diff --git a/server/src/account.rs b/server/src/account.rs
index 3b85e4da..3a01285a 100644
--- a/server/src/account.rs
+++ b/server/src/account.rs
@@ -99,7 +99,6 @@ pub fn account_login(name: &String, password: &String, tx: &mut Transaction) ->
SELECT id, password, name
FROM accounts
WHERE name = $1
- RETURNING id, name;
";
let result = tx
@@ -134,7 +133,7 @@ pub fn account_login(name: &String, password: &String, tx: &mut Transaction) ->
account_set_token(tx, &account)
}
-fn account_set_token(tx: &mut Transaction, account: &Account) -> Result {
+pub fn account_set_token(tx: &mut Transaction, account: &Account) -> Result {
let mut rng = thread_rng();
let token: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
diff --git a/server/src/net.rs b/server/src/net.rs
index 0f4973df..dd9905e0 100644
--- a/server/src/net.rs
+++ b/server/src/net.rs
@@ -17,7 +17,7 @@ use r2d2_postgres::{TlsMode, PostgresConnectionManager};
use rpc::{receive, RpcResult, RpcErrorResponse, AccountLoginParams, AccountCreateParams};
use warden::{warden};
-use account::{Account, account_login, account_create, account_from_token};
+use account::{Account, account_login, account_create, account_from_token, account_set_token};
pub type Db = PooledConnection;
type PgPool = Pool;
@@ -174,6 +174,17 @@ fn token_res(token: String, secure: bool) -> HttpResponse {
.finish()
}
+fn token_clear() -> HttpResponse {
+ HttpResponse::Ok()
+ .cookie(Cookie::build("x-auth-token", "")
+ // .secure(secure)
+ .http_only(true)
+ .same_site(SameSite::Strict)
+ .max_age(-1) // 1 week aligns with db set
+ .finish())
+ .finish()
+}
+
fn login(state: web::Data, params: web::Json::) -> Result {
let db = state.pool.get().or(Err(MnmlError::ServerError))?;
let mut tx = db.transaction().or(Err(MnmlError::ServerError))?;
@@ -190,6 +201,24 @@ fn login(state: web::Data, params: web::Json::) -> Re
}
}
+fn logout(r: HttpRequest, state: web::Data) -> Result {
+ match r.cookie("x-auth-token") {
+ Some(t) => {
+ let db = state.pool.get().or(Err(MnmlError::ServerError))?;
+ let mut tx = db.transaction().or(Err(MnmlError::ServerError))?;
+ match account_from_token(t.value().to_string(), &mut tx) {
+ Ok(a) => {
+ account_set_token(&mut tx, &a).or(Err(MnmlError::Unauthorized))?;
+ tx.commit().or(Err(MnmlError::ServerError))?;
+ return Ok(token_clear());
+ },
+ Err(_) => Err(MnmlError::Unauthorized),
+ }
+ },
+ None => Err(MnmlError::Unauthorized),
+ }
+}
+
fn register(state: web::Data, params: web::Json::) -> Result {
let db = state.pool.get().or(Err(MnmlError::ServerError))?;
let mut tx = db.transaction().or(Err(MnmlError::ServerError))?;
@@ -235,6 +264,7 @@ pub fn start() {
.wrap(middleware::Logger::default())
.wrap(Cors::new().supports_credentials())
.service(web::resource("/login").route(web::post().to(login)))
+ .service(web::resource("/logout").route(web::post().to(logout)))
.service(web::resource("/register").route(web::post().to(register)))
.service(web::resource("/ws/").route(web::get().to(connect))))
.bind("127.0.0.1:40000").expect("could not bind to port")
@@ -245,6 +275,7 @@ pub fn start() {
.data(State { pool: pool.clone(), secure: true })
.wrap(middleware::Logger::default())
.service(web::resource("/login").route(web::post().to(login)))
+ .service(web::resource("/logout").route(web::post().to(logout)))
.service(web::resource("/register").route(web::post().to(register)))
.service(web::resource("/ws/").route(web::get().to(connect))))
.bind("127.0.0.1:40000").expect("could not bind to port")