From 814021eeac5f02f057541932416f523d68b1487c Mon Sep 17 00:00:00 2001
From: JesusPerez <jpl@jesusperez.pro>
Date: Tue, 22 Feb 2022 20:31:44 +0000
Subject: [PATCH] chore: add code

---
 Cargo.toml               |  79 +++++
 src/defs.rs              |  98 ++++++
 src/handlers.rs          |   9 +
 src/handlers/jwt.rs      | 196 ++++++++++++
 src/handlers/kratos.rs   | 365 ++++++++++++++++++++++
 src/handlers/router.rs   |  68 +++++
 src/handlers/sessions.rs | 133 ++++++++
 src/kratos.rs            | 633 +++++++++++++++++++++++++++++++++++++++
 src/main.rs              | 413 +++++++++++++++++++++++++
 src/state.rs             |  66 ++++
 src/utils.rs             |   7 +
 src/utils/reqenv.rs      | 141 +++++++++
 src/utils/reqtasks.rs    | 453 ++++++++++++++++++++++++++++
 13 files changed, 2661 insertions(+)
 create mode 100644 Cargo.toml
 create mode 100644 src/defs.rs
 create mode 100644 src/handlers.rs
 create mode 100644 src/handlers/jwt.rs
 create mode 100644 src/handlers/kratos.rs
 create mode 100644 src/handlers/router.rs
 create mode 100644 src/handlers/sessions.rs
 create mode 100644 src/kratos.rs
 create mode 100644 src/main.rs
 create mode 100644 src/state.rs
 create mode 100644 src/utils.rs
 create mode 100644 src/utils/reqenv.rs
 create mode 100644 src/utils/reqtasks.rs

diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..2b29215
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,79 @@
+[package]
+name = "libresignin"
+version = "0.1.0"
+authors = ["JesusPerez <jpl@jesusperez.pro>"]
+edition = "2021"
+description= "Singe Sing On Services for LibreCloud"
+license-file = "LICENSE"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[profile.dev]
+opt-level = 0
+
+[profile.release]
+opt-level = 3
+
+[dependencies]
+anyhow="1.0"
+async-session = "3.0"
+axum = { version = "0.4", features = ["headers"] }
+axum-server = { version = "0.3", features = ["tls-rustls"] }
+base64 = "0.13"
+bytes = "1.1"
+casbin = "2.0"
+chrono = "0.4"
+dotenv = "0.15"
+envmnt = "0.9"
+glob = "0.3"
+hyper = { version = "0.14", features = ["full"] }
+#reqwest = "0.11"
+
+bcrypt = "0.10"
+tower-cookies = { version = "0.5", features = ["signed"] }
+cookie = { version = "0.16", features = ["percent-encode"] }
+
+reqwest-middleware = "0.1"
+reqwest-retry = "0.1"
+reqwest-tracing = "0.2"
+
+cookie_store = "0.15"
+reqwest_cookie_store = "0.2"
+reqwest = { version = "0.11", features = ["rustls-tls","cookies","json"], default-features = false }
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+thiserror = "1.0"
+tracing = "0.1"
+tracing-subscriber = { version="0.3", features = ["env-filter"] }
+tokio = { version = "1.16", features = ["full"] }
+tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] }
+tower-http = { version = "0.2", features = ["fs", "cors", "trace", "add-extension", "auth", "compression-full"] }
+
+uuid = { version = "0.8", features = ["v4", "serde"] }
+
+tera = "1.15"
+
+headers = "0.3"
+jsonwebtoken = "8.0"
+once_cell = "1.8"
+
+redis = { version = "0.21", features = [ "tokio-comp", "cluster"] }
+redis-graph = { version = "0.4", features = ['tokio-comp'] }
+sqlx = {version = "0.5", default-features = false, features = ["macros","runtime-tokio-rustls","sqlite", "mysql", "postgres", "decimal", "chrono"]}
+pretty_env_logger = "0.4"
+
+webenv = { version = "0.1.2", path = "../rust_lib/webenv" }
+app_tools = { version = "0.1.0", path = "../rust_lib/utils/app_tools" }
+app_env = { version = "0.1.0", path = "../rust_lib/defs/app_env" }
+datastores = { version = "0.1.0", path = "../rust_lib/datastores/defs" }
+connectors = { version = "0.1.0", path = "../rust_lib/datastores/connectors" }
+app_auth = { version = "0.1.0", path = "../rust_lib/defs/app_auth" }
+app_errors = { version = "0.1.0", path = "../rust_lib/defs/app_errors" }
+# gql_playground = { version = "0.1.0", path = "../rust_lib/graphql/gql_playground" }
+key_of_life = { path = "../rust_lib/key_of_life" }
+
+[dev-dependencies]
+pretty_env_logger = "0.4"
+tracing-subscriber = "0.3.6"
+tracing-log = "0.1"
diff --git a/src/defs.rs b/src/defs.rs
new file mode 100644
index 0000000..a8b0023
--- /dev/null
+++ b/src/defs.rs
@@ -0,0 +1,98 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+// use serde::{Serialize, Deserialize};
+// use std::collections::{HashMap, BTreeMap};
+// use app_env::{appenv::AppEnv, AppStore};
+use app_env::{AppStore};
+use app_auth::{AuthStore};
+
+use std::sync::Arc;
+use tokio::sync::Mutex;
+use connectors::defs::{AppDataConn}; 
+
+/*
+use kloud::utils::load_from_module;
+use clds::clouds::defs::{
+	CloudEnv,
+	Cloud,
+  SrvcsHostInfOut,
+	InfoStatus,
+};
+use kloud::kloud::Kloud;
+
+#[derive(Clone,Default)]
+pub struct CollsData {
+  pub klouds: KloudStore<Kloud>,
+}
+
+impl CollsData {
+  pub fn new(env: AppEnv,verbose: isize) -> Self {
+    //  dbg!(&env.contexts);
+    let (klouds_frmt, klouds_content) = load_from_module(env.to_owned(),"klouds");
+    Self {
+      klouds: KloudStore::new(
+        Kloud::entries(&klouds_content,&klouds_frmt),
+        "klouds".to_owned(),
+        DataContext::default(),
+				verbose
+      ),
+    }
+  }
+  pub async fn get_klouds_entries(coll_map: CollsData) -> BTreeMap<String,Kloud> {
+    let mut result = BTreeMap::new(); 
+    let cur = coll_map.klouds.entries.read();
+    for (key,value) in cur.iter() {
+      result.insert(key.to_owned(), value.to_owned());
+    }
+    result
+  }
+}
+*/
+
+#[derive(Clone)]
+pub struct AppDBs {
+//  pub colls: CollsData,
+  pub app: AppStore,
+}
+
+#[derive(Clone)]
+pub struct DataDBs {
+//  pub colls: CollsData,
+  pub app: AppStore,
+  pub auth: AuthStore,
+	pub conns: Arc<Mutex<AppDataConn>>,
+}
+
+/*
+pub async fn load_cloud_env(cloud: &mut Cloud) {
+	let force: u8 = "-f".as_bytes()[0];
+  cloud.env = CloudEnv::new(force,load_key().await);
+  cloud.providers = Cloud::load_providers().await;
+}
+*/ 
+
+pub const KEY_PATH: &str = ".k";
+use key_of_life::get_key;
+
+pub async fn load_key() -> String {
+  let key_path = envmnt::get_or("KEY_PATH", KEY_PATH);
+  let key = get_key(&key_path,None).await;
+  if key.is_empty() {
+    std::process::exit(0x0100);
+  }
+  key
+}
+/*
+pub type MapCheckInfo =	BTreeMap<String,Vec<SrvcsHostInfOut>>;
+
+#[derive(Clone, Debug, Serialize,  Deserialize, Default)]
+pub struct KldCheck {
+	pub name: String,
+	pub liveness: HashMap<String, MapCheckInfo>,
+	pub apps: HashMap<String, MapCheckInfo>,
+	pub infos: Vec<InfoStatus>,
+}
+*/
\ No newline at end of file
diff --git a/src/handlers.rs b/src/handlers.rs
new file mode 100644
index 0000000..1319bad
--- /dev/null
+++ b/src/handlers.rs
@@ -0,0 +1,9 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+pub mod jwt;
+pub mod sessions;
+pub mod router;
+pub mod kratos;
\ No newline at end of file
diff --git a/src/handlers/jwt.rs b/src/handlers/jwt.rs
new file mode 100644
index 0000000..5f2f65c
--- /dev/null
+++ b/src/handlers/jwt.rs
@@ -0,0 +1,196 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+
+//! Example JWT authorization/authentication.
+//!
+//! Run with
+//!
+//! ```not_rust
+//! JWT_SECRET=secret cargo run -p example-jwt
+//! ```
+
+use axum::{
+    async_trait,
+    extract::{FromRequest, RequestParts, TypedHeader},
+    headers::{authorization::Bearer, Authorization},
+    http::StatusCode,
+    response::{IntoResponse, Response},
+    Json,
+};
+use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
+use once_cell::sync::Lazy;
+use serde::{Deserialize, Serialize};
+use serde_json::json;
+use std::{fmt::Display};
+
+// Quick instructions
+//
+// - get an authorization token:
+//
+// curl -s \
+//     -w '\n' \
+//     -H 'Content-Type: application/json' \
+//     -d '{"client_id":"foo","client_secret":"bar"}' \
+//     http://localhost:3000/authorize
+//
+// - visit the protected area using the authorized token
+//
+// curl -s \
+//     -w '\n' \
+//     -H 'Content-Type: application/json' \
+//     -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUiLCJleHAiOjEwMDAwMDAwMDAwfQ.M3LAZmrzUkXDC1q5mSzFAs_kJrwuKz3jOoDmjJ0G4gM' \
+//     http://localhost:3000/protected
+//
+// - try to visit the protected area using an invalid token
+//
+// curl -s \
+//     -w '\n' \
+//     -H 'Content-Type: application/json' \
+//     -H 'Authorization: Bearer blahblahblah' \
+//     http://localhost:3000/protected
+
+static KEYS: Lazy<Keys> = Lazy::new(|| {
+    let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");
+    Keys::new(secret.as_bytes())
+});
+
+pub async fn protected(claims: Claims) -> Result<String, AuthError> {
+    // Send the protected data to the user
+    Ok(format!(
+        "Welcome to the protected area :)\nYour data:\n{}",
+        claims
+    ))
+}
+
+pub async fn getjwt() -> Result<Json<AuthBody>, AuthError> {
+    let claims = Claims {
+        sub: "b@b.com".to_owned(),
+        company: "ACME".to_owned(),
+        exp: 100000,
+    };
+    // Create the authorization token
+    let token = encode(&Header::default(), &claims, &KEYS.encoding)
+        .map_err(|_| AuthError::TokenCreation)?;
+
+    // Send the authorized token
+    Ok(Json(AuthBody::new(token)))
+}
+
+pub async fn authorize(Json(payload): Json<AuthPayload>) -> Result<Json<AuthBody>, AuthError> {
+    // Check if the user sent the credentials
+    if payload.client_id.is_empty() || payload.client_secret.is_empty() {
+        return Err(AuthError::MissingCredentials);
+    }
+    // Here you can check the user credentials from a database
+    if payload.client_id != "foo" || payload.client_secret != "bar" {
+        return Err(AuthError::WrongCredentials);
+    }
+    let claims = Claims {
+        sub: "b@b.com".to_owned(),
+        company: "ACME".to_owned(),
+        exp: 100000,
+    };
+    // Create the authorization token
+    let token = encode(&Header::default(), &claims, &KEYS.encoding)
+        .map_err(|_| AuthError::TokenCreation)?;
+
+    // Send the authorized token
+    Ok(Json(AuthBody::new(token)))
+}
+
+impl Display for Claims {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    write!(f, "Email: {}\nCompany: {}", self.sub, self.company)
+  }
+}
+
+impl AuthBody {
+	fn new(access_token: String) -> Self {
+		Self {
+			access_token,
+			token_type: "Bearer".to_string(),
+		}
+	}
+}
+
+#[async_trait]
+impl<B> FromRequest<B> for Claims
+where
+    B: Send,
+{
+    type Rejection = AuthError;
+
+    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
+        // Extract the token from the authorization header
+        let TypedHeader(Authorization(bearer)) =
+            TypedHeader::<Authorization<Bearer>>::from_request(req)
+                .await
+                .map_err(|_| AuthError::InvalidToken)?;
+        // Decode the user data
+			  dbg!("{:#?}",bearer.token());	
+        dbg!("{:#?}",decode::<Claims>(bearer.token(), &KEYS.decoding, &Validation::default()));
+        let token_data = decode::<Claims>(bearer.token(), &KEYS.decoding, &Validation::default())
+            .map_err(|_| AuthError::InvalidToken)?;
+
+        Ok(token_data.claims)
+    }
+}
+
+impl IntoResponse for AuthError {
+    fn into_response(self) -> Response {
+        let (status, error_message) = match self {
+            AuthError::WrongCredentials => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
+            AuthError::MissingCredentials => (StatusCode::BAD_REQUEST, "Missing credentials"),
+            AuthError::TokenCreation => (StatusCode::INTERNAL_SERVER_ERROR, "Token creation error"),
+            AuthError::InvalidToken => (StatusCode::BAD_REQUEST, "Invalid token"),
+        };
+        let body = Json(json!({
+            "error": error_message,
+        }));
+        (status, body).into_response()
+    }
+}
+
+struct Keys {
+    encoding: EncodingKey,
+    decoding: DecodingKey,
+}
+
+impl Keys {
+    fn new(secret: &[u8]) -> Self {
+        Self {
+            encoding: EncodingKey::from_secret(secret),
+            decoding: DecodingKey::from_secret(secret),
+        }
+    }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Claims {
+  pub sub: String,
+  pub company: String,
+  pub exp: usize,
+}
+
+#[derive(Debug, Serialize)]
+pub struct AuthBody {
+  pub access_token: String,
+  pub token_type: String,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct AuthPayload {
+  client_id: String,
+  client_secret: String,
+}
+
+#[derive(Debug)]
+pub enum AuthError {
+  WrongCredentials,
+  MissingCredentials,
+  TokenCreation,
+  InvalidToken,
+}
diff --git a/src/handlers/kratos.rs b/src/handlers/kratos.rs
new file mode 100644
index 0000000..2e02277
--- /dev/null
+++ b/src/handlers/kratos.rs
@@ -0,0 +1,365 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+
+use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
+use reqwest_retry::{RetryTransientMiddleware, policies::ExponentialBackoff};
+use reqwest_tracing::TracingMiddleware;
+use serde::{Deserialize}; //, Serialize};
+use axum::{
+	response::Html,
+	routing::get,
+	// routing::post,
+	Router,
+	//Json,
+	response::IntoResponse,
+	extract::{Extension,Path,Query},
+	//http::{Request, header::HeaderMap, Method,StatusCode},
+	http::{header::HeaderMap,StatusCode},
+  // body::{Bytes, Body},
+};
+// use serde::{Deserialize, Serialize};
+use crate::defs::{DataDBs}; 
+use crate::utils::reqenv::ReqEnv;
+use std::{
+	collections::HashMap,
+  sync::Arc,
+};
+
+use reqwest_cookie_store::CookieStoreMutex;
+
+use crate::kratos::{
+	get_root_url,
+	get_flow_csrf,
+	// load_idschema,
+ 	// KratosUser,
+	// KratosItemProps,
+	// KratosTraitItem,
+  // JsonMap,
+	load_traits,
+	set_kratos_user,
+	get_kratos_user,
+	set_kratos_login,
+	on_kratos_user,
+	logout_kratos_user,
+//	KratosSessionResponse,
+//	KratosReqId,
+// 	KratosResponse,
+};
+
+// use cookie::{Cookie};
+// use tower_cookies::{Cookies};
+
+pub async fn run(client: ClientWithMiddleware) {
+    client
+        .get("https://truelayer.com")
+        .header("foo", "bar")
+        .send()
+        .await
+        .unwrap();
+}
+
+// pub async fn registration_handler(cookies: Cookies,header: HeaderMap, Extension(dbs): Extension<DataDBs>, Extension(req_cli): Extension<Arc<CookieStoreMutex>>) -> Html<&'static str> {
+pub async fn registration_handler(header: HeaderMap, Extension(dbs): Extension<DataDBs>,Extension(req_cli): Extension<Arc<CookieStoreMutex>>) ->  impl IntoResponse {
+  let reqenv = ReqEnv::new(dbs.app, dbs.auth, header, "get", "/login", "html", "rt");
+	let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
+  let client = ClientBuilder::new(reqwest::Client::new())
+     // Trace HTTP requests. See the tracing crate to make use of these traces.
+    .with(TracingMiddleware)
+    // Retry failed requests.
+    .with(RetryTransientMiddleware::new_with_policy(retry_policy))
+    .build();
+
+  //  run(client).await;
+
+//  let reqwest_client = Client::builder().build().unwrap();
+//     let client = ClientBuilder::new(reqwest_client)
+//         .with(LoggingMiddleware)
+//         .build();
+//     let resp = client.get("https://truelayer.com").send().await.unwrap();
+//     println!("TrueLayer page HTML: {}", resp.text().await.unwrap());
+
+
+	// // dbg!("{:#?}",reqenv.websrvr());	
+	let root_url = get_root_url(reqenv.websrvr().signin.protocol,reqenv.websrvr().signin.root,reqenv.websrvr().signin.port);
+	let flow = String::from("self-service/registration");
+	// //let flow = String::from("login");
+	let query = String::from("/api?refresh=false&aal=&return_to=");
+  let traits = load_traits(reqenv.websrvr().signin.idschema_path);
+  // // dbg!("{:#?}",&traits);	
+	let mut user_data = HashMap::new();
+	user_data.insert("name.first".to_string(),"Jesús".to_string());
+	user_data.insert("name.last".to_string(),"Pérez".to_string());
+	user_data.insert("username".to_string(),"jesuspl".to_string());
+	user_data.insert("password".to_string(),"19Ting22".to_string());
+	user_data.insert("email".to_string(),"jpl@jesusperez.pro".to_string());
+	let user = set_kratos_user("traits",user_data,traits);
+  // // println!("{}",&user);	
+	let url=format!("{}/{}{}",&root_url,flow,query);
+	let html:String;
+	// match get_flow_csrf(reqenv.websrvr().signin,&req_cli,url.as_str()).await {
+	match get_flow_csrf("",reqenv.websrvr().signin,&req_cli,"","",url.as_str()).await {
+		Ok(reqid) => {
+			println!("flow: {}",&reqid.flow);
+			println!("csrf_token: {}",&reqid.csrf);
+			println!("cookie: {}",&reqid.cookie);
+			let query_id = format!("?flow={}",&reqid.flow);
+			let reg_url=format!("{}/{}{}",&root_url,flow,query_id);
+			match on_kratos_user("",reqenv.websrvr().signin,&req_cli,&reqid.csrf,&reqid.cookie,&reg_url,user).await {
+				Ok(res) => {
+					println!("Result {:?}",res.status);
+					html = format!(
+					r#"
+					 <h1>Register</h1>
+				   <a href='{}/login'>login</a>
+					 <div>Status: {} </div>
+			   	"#,&reqenv.websrvr_url(),res.status);
+				},
+				Err(e) => {
+	        eprintln!("{}",e);
+					html = format!(
+					r#"
+					 <h1>Login</h1>
+					 <div>Error: {} </div>
+			   	"#,e);
+				}
+			}
+		},
+		Err(e) => {
+	    eprintln!("{}",e);
+			html = format!(
+			r#"
+				<h1>Login</h1>
+				<div>Error: {} </div>
+			"#,e);
+		},
+	}
+	Html(html)
+}
+pub async fn login_handler(header: HeaderMap, Extension(dbs): Extension<DataDBs>,Extension(req_cli): Extension<Arc<CookieStoreMutex>>) -> impl IntoResponse {
+  let reqenv = ReqEnv::new(dbs.app.to_owned(), dbs.auth.to_owned(), header.to_owned(), "get", "/login", "html", "rt");
+	// let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
+  // let client = ClientBuilder::new(reqwest::Client::new())
+  //    // Trace HTTP requests. See the tracing crate to make use of these traces.
+  //   .with(TracingMiddleware)
+  //   // Retry failed requests.
+  //   .with(RetryTransientMiddleware::new_with_policy(retry_policy))
+  //   .build();
+
+  //  run(client).await;
+
+//  let reqwest_client = Client::builder().build().unwrap();
+//     let client = ClientBuilder::new(reqwest_client)
+//         .with(LoggingMiddleware)
+//         .build();
+//     let resp = client.get("https://truelayer.com").send().await.unwrap();
+//     println!("TrueLayer page HTML: {}", resp.text().await.unwrap());
+
+
+	// // dbg!("{:#?}",reqenv.websrvr());	
+	let flow = String::from("self-service/login");
+	let query = String::from("/api?refresh=false&aal=&return_to=");
+	let root_url = get_root_url(reqenv.websrvr().signin.protocol,reqenv.websrvr().signin.root,reqenv.websrvr().signin.port);
+	let mut user_data = HashMap::new();
+	user_data.insert("password".to_string(),"19Ting22".to_string());
+	user_data.insert("password_identifier".to_string(),"jesuspl".to_string());
+	let user = set_kratos_login(user_data);
+	let url=format!("{}/{}{}",&root_url,flow,query);
+	// match get_flow_csrf(reqenv.websrvr().signin,&req_cli,url.as_str()).await {
+	let html:String;
+	match get_flow_csrf("",reqenv.websrvr().signin,&req_cli,"","",url.as_str()).await {
+		Ok(reqid) => {
+			println!("flow: {}",&reqid.flow);
+			println!("csrf_token: {}",&reqid.csrf);
+			println!("cookie: {}",&reqid.cookie);
+			let query_id = format!("?flow={}",&reqid.flow);
+			let reg_url=format!("{}/{}{}",&root_url,flow,query_id);
+			match on_kratos_user("",reqenv.websrvr().signin,&req_cli,&reqid.csrf,&reqid.cookie,&reg_url,user).await {
+				Ok(res) => {
+					println!("Result {:?}",res.status);
+					//    res.session_resp.session.identity.id,
+					// 	 res.session_resp.session_token);
+          // return html.to_owned();
+					if res.status == StatusCode::OK {
+						html = format!(
+						r#"
+						<h1>Login</h1>
+						<div><a href='{}/whoami/{}?token={}'>who am i ?</a></div>
+						<div><a href='{}/logout/{}?token={}'>logout</a></div>
+						<div>Status: {} </div>
+						"#,
+						&reqenv.websrvr_url(),res.session_resp.session.identity.id, res.session_resp.session_token,
+						&reqenv.websrvr_url(),res.session_resp.session.identity.id, res.session_resp.session_token,
+						res.status);	
+					} else {
+						html = format!(
+						r#"
+						<h1>Login</h1>
+						<div>Status: {} </div>
+					  <div><a href='{}/registration'>Registration</a></div>
+						"#,res.status,&reqenv.websrvr_url());	
+					}
+				},
+				Err(e) => {
+	        eprintln!("{}",e);
+					html = format!(
+					r#"
+					 <h1>Login</h1>
+					 <div><a href='{}/registration'>Registration</a></div>
+					 <div>Error: {} </div>
+			   	"#,&reqenv.websrvr_url(),e);
+				}
+			}
+		},
+		Err(e) => {
+	    eprintln!("{}",e);
+			html = format!(
+			r#"
+				<h1>Login</h1>
+				<div>Error: {} </div>
+			"#,e);
+		},
+	};
+	Html(html)
+}
+pub async fn recovery_handler() -> Html<&'static str> {
+    Html("<h1>Hello, World!</h1>")
+}
+pub async fn whoami(id: String, token: String,
+	header: HeaderMap, dbs: DataDBs,req_cli: Arc<CookieStoreMutex>) -> String {
+	let reqenv = ReqEnv::new(dbs.app, dbs.auth, header, "get", "/login", "html", "rt");
+	let flow = String::from("self-service/login");
+	let query = String::from("/api?refresh=false&aal=&return_to=");
+	let root_url = get_root_url(reqenv.websrvr().signin.protocol,reqenv.websrvr().signin.root,reqenv.websrvr().signin.port);
+	let url=format!("{}/{}{}",&root_url,flow,query);
+	let html:String;
+	// match get_flow_csrf(reqenv.websrvr().signin,&req_cli,url.as_str()).await {
+	match get_flow_csrf("",reqenv.websrvr().signin,&req_cli,"","",url.as_str()).await {
+		Ok(reqid) => {
+			println!("flow: {}",&reqid.flow);
+			println!("csrf_token: {}",&reqid.csrf);
+			println!("cookie: {}",&reqid.cookie);
+	    let flow_check = String::from("sessions/whoami");
+			let query_id = format!("?flow={}",&reqid.flow);
+			let reg_url=format!("{}/{}{}",&root_url,flow_check,query_id);
+			// match on_kratos_user(reqenv.websrvr().signin,&reqid.csrf,&id,&reg_url,user).await {
+			match get_kratos_user(&id,&token,reqenv.websrvr().signin,&req_cli,&reqid.csrf,&reqid.cookie,&reg_url).await {
+				Ok(res) => {
+					println!("Result {:?}",res.status);
+					if res.status == StatusCode::OK {
+						html = format!(
+						r#"
+						<h1>whoami</h1>
+						<div>Status: {}</div>
+						<div><a href='{}/logout/{}?token={}'>logout</a></div>
+						"#,res.status,
+							&reqenv.websrvr_url(),id,token)
+					} else {
+						html = format!(
+						r#"
+						<h1>whoami</h1>
+						<div>Status: {}</div>
+						<div><a href='{}/login'>login</a></div>
+						"#,res.status, &reqenv.websrvr_url())
+					}
+				},
+				Err(e) => {
+	        eprintln!("{}",e);
+					html = format!(
+					r#"
+						<h1>whoami</h1>
+					  <div><a href='{}/login'>Login</a></div>
+						<div>Error: {} </div>
+					"#,&reqenv.websrvr_url(),e);
+				}
+			}
+		},
+		Err(e) => {
+	    eprintln!("{}",e);
+			html = format!(
+			r#"
+				<h1>whoami</h1>
+				<div>Error: {} </div>
+			"#,e);
+		},
+	};
+	html
+}
+#[derive(Deserialize)]
+pub struct QueryToken {
+	pub token: String,
+}
+pub async fn whoami_handler(Path(id): Path<String>, query: Query<QueryToken>,
+	header: HeaderMap, Extension(dbs): Extension<DataDBs>,Extension(req_cli): Extension<Arc<CookieStoreMutex>>) -> impl IntoResponse {
+  Html(whoami(id,query.token.to_string(),header,dbs,req_cli).await)
+}
+ pub async fn logout_handler(Path(id): Path<String>, urlquery: Query<QueryToken>, header: HeaderMap, Extension(dbs): Extension<DataDBs>,Extension(req_cli): Extension<Arc<CookieStoreMutex>>) -> impl IntoResponse {
+	let reqenv = ReqEnv::new(dbs.app, dbs.auth, header, "get", "/login", "html", "rt");
+	let flow = String::from("self-service/login");
+	let query = format!("/api?refresh=false&aal=&return_to=&query={}",urlquery.token.to_string());
+	let root_url = get_root_url(reqenv.websrvr().signin.protocol,reqenv.websrvr().signin.root,reqenv.websrvr().signin.port);
+	let url=format!("{}/{}{}",&root_url,flow,query);
+	let html:String;
+	// match get_flow_csrf(reqenv.websrvr().signin,&req_cli,url.as_str()).await {
+	match get_flow_csrf("",reqenv.websrvr().signin,&req_cli,"","",url.as_str()).await {
+		Ok(reqid) => {
+		println!("flow: {}",&reqid.flow);
+		println!("csrf_token: {}",&reqid.csrf);
+		println!("cookie: {}",&reqid.cookie);
+		let query_id = format!("/api?{}&flow={}",&urlquery.token,&reqid.flow);
+		let reg_url=format!("{}/{}{}",&root_url,flow,query_id);
+		// match on_kratos_user(reqenv.websrvr().signin,&reqid.csrf,&id,&reg_url,user).await {
+		match logout_kratos_user(&id,&urlquery.token.to_string(),reqenv.websrvr().signin,&req_cli,&reqid.csrf,&reqid.cookie,&reg_url).await {
+			Ok(res) => {
+				println!("Result {:?}",res.status);
+				html = format!(
+				r#"
+				<h1>Logout</h1>
+				<a href='{}/login'>login</a>
+				<div>Status: {}</div>
+				"#,reqenv.websrvr_url(),res.status);
+			},
+			Err(e) => {
+				eprintln!("{}",e);
+				html = format!(
+				r#"
+					<h1>Logout</h1>
+					<div>Error: {} </div>
+				"#,e);
+			}
+		};
+	},
+	Err(e) => {
+			eprintln!("{}",e);
+			html = format!(
+			r#"
+				<h1>Logout</h1>
+				<div>Error: {} </div>
+			"#,e);
+		},
+	};
+	Html(html)
+}
+pub async fn verification_handler() -> Html<&'static str> {
+   Html("<h1>Hello, World!</h1>")
+}
+pub async fn settings_handler() -> Html<&'static str> {
+    Html("<h1>Hello, World!</h1>")
+}
+pub async fn welcome_handler() -> Html<&'static str> {
+    Html("<h1>Hello, World!</h1>")
+}
+
+pub fn def_handlers(web_router: Router) -> Router {
+  web_router
+  .route("/login", get(login_handler))
+  .route("/registration", get(registration_handler))
+  .route("/recovery", get(recovery_handler))
+  .route("/whoami/:id", get(whoami_handler))
+  .route("/logout/:id", get(logout_handler))
+  .route("/verification", get(verification_handler))
+  .route("/settings", get(settings_handler))
+  .route("/welcome", get(welcome_handler))
+}
\ No newline at end of file
diff --git a/src/handlers/router.rs b/src/handlers/router.rs
new file mode 100644
index 0000000..30b8fc8
--- /dev/null
+++ b/src/handlers/router.rs
@@ -0,0 +1,68 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+use axum::{
+	response::Html,
+	routing::get,
+	routing::post,
+	Router,
+	// extract::{self,Extension,Path,Query},
+	extract::{Extension,Path},
+	http::{header::HeaderMap, StatusCode},
+  // body::{Bytes, Body},
+	response::IntoResponse,
+	BoxError,
+};
+use std::{
+  borrow::Cow,
+};
+use crate::defs::{DataDBs}; 
+use crate::utils::reqenv::ReqEnv;
+
+// pub async fn html_handler(Path(path): Path<String>,query: Option<Query<String>>, Extension(dbs): Extension<DataDBs>) -> Html<&'static str> {
+// pub async fn html_handler(Path(path): Path<String>,Extension(dbs): Extension<DataDBs>) -> Html<&'static str> {
+pub async fn html_handler(header: HeaderMap, Path(name): Path<String>,Extension(dbs): Extension<DataDBs>) -> Html<&'static str> {
+ let reqenv = ReqEnv::new(dbs.app, dbs.auth, header, "get", "/hello", "html", "rt");
+  dbg!("{:#?}",reqenv.user_authentication().await);
+// dbg!("{:#?}",headers);
+//dbg!("{:#?}",name);	
+//dbg!("{:#?}",reqenv.config());	
+// 	dbg!("{:#?}",query);	
+// 	dbg!("{:#?}",dbs.auth.users);	
+  Html("<h1>Hello, World!</h1>")
+}
+pub async fn alive_handler() -> Html<&'static str> {
+    Html("ok")
+}
+pub async fn ready_handler() -> Html<&'static str> {
+    Html("ok")
+}
+pub async fn handle_error(error: BoxError) -> impl IntoResponse {
+    if error.is::<tower::timeout::error::Elapsed>() {
+        return (StatusCode::REQUEST_TIMEOUT, Cow::from("request timed out"));
+    }
+    if error.is::<tower::load_shed::error::Overloaded>() {
+        return (
+            StatusCode::SERVICE_UNAVAILABLE,
+            Cow::from("service is overloaded, try again later"),
+        );
+    }
+    (
+        StatusCode::INTERNAL_SERVER_ERROR,
+        Cow::from(format!("Unhandled internal error: {}", error)),
+    )
+}
+pub fn router_handlers(web_router: Router) -> Router {
+	let mut webrouter = web_router.to_owned();
+	webrouter = crate::handlers::kratos::def_handlers(webrouter);
+  webrouter
+  .route("/getjwt", get(crate::handlers::jwt::getjwt))
+  .route("/protected", get(crate::handlers::jwt::protected))
+  .route("/authorize", post(crate::handlers::jwt::authorize))
+  .route("/session", get(crate::handlers::sessions::handler))
+  .route("/hello/:name", get(html_handler))
+	.route("/health/alive", get(alive_handler))
+  .route("/health/ready", get(ready_handler))
+}
diff --git a/src/handlers/sessions.rs b/src/handlers/sessions.rs
new file mode 100644
index 0000000..bfe9ad7
--- /dev/null
+++ b/src/handlers/sessions.rs
@@ -0,0 +1,133 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+use async_session::{MemoryStore, Session, SessionStore as _};
+use axum::{
+    async_trait,
+    extract::{Extension, FromRequest, RequestParts, TypedHeader},
+    headers::Cookie,
+    http::{
+        self,
+        header::{HeaderMap, HeaderValue},
+        StatusCode,
+    },
+    response::IntoResponse,
+    // routing::get,
+    // AddExtensionLayer, Router,
+};
+use serde::{Deserialize, Serialize};
+use std::fmt::Debug;
+use uuid::Uuid;
+
+pub async fn handler(user_id: UserIdFromSession) -> impl IntoResponse {
+    let (headers, user_id, create_cookie) = match user_id {
+        UserIdFromSession::FoundUserId(user_id) => (HeaderMap::new(), user_id, false),
+        UserIdFromSession::CreatedFreshUserId(new_user) => {
+            let mut headers = HeaderMap::new();
+            headers.insert(http::header::SET_COOKIE, new_user.cookie);
+            (headers, new_user.user_id, true)
+        }
+    };
+
+    tracing::debug!("handler: user_id={:?} send_headers={:?}", user_id, headers);
+
+    (
+        headers,
+        format!(
+            "user_id={:?} session_cookie_name={} create_new_session_cookie={}",
+            user_id, crate::AXUM_SESSION_COOKIE_NAME, create_cookie
+        ),
+    )
+}
+
+pub struct FreshUserId {
+  pub user_id: UserId,
+  pub cookie: HeaderValue,
+}
+
+pub enum UserIdFromSession {
+  FoundUserId(UserId),
+  CreatedFreshUserId(FreshUserId),
+}
+
+#[async_trait]
+impl<B> FromRequest<B> for UserIdFromSession
+where
+    B: Send,
+{
+    type Rejection = (StatusCode, &'static str);
+
+    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
+        let Extension(store) = Extension::<MemoryStore>::from_request(req)
+            .await
+            .expect("`MemoryStore` extension missing");
+
+        let cookie = Option::<TypedHeader<Cookie>>::from_request(req)
+            .await
+            .unwrap();
+
+        let session_cookie = cookie
+            .as_ref()
+            .and_then(|cookie| cookie.get(crate::AXUM_SESSION_COOKIE_NAME));
+
+        // return the new created session cookie for client
+        if session_cookie.is_none() {
+            let user_id = UserId::new();
+            let mut session = Session::new();
+            session.insert("user_id", user_id).unwrap();
+            let cookie = store.store_session(session).await.unwrap().unwrap();
+            return Ok(Self::CreatedFreshUserId(FreshUserId {
+                user_id,
+                cookie: HeaderValue::from_str(
+                    format!("{}={}", crate::AXUM_SESSION_COOKIE_NAME, cookie).as_str(),
+                )
+                .unwrap(),
+            }));
+        }
+
+        tracing::debug!(
+            "UserIdFromSession: got session cookie from user agent, {}={}",
+            crate::AXUM_SESSION_COOKIE_NAME,
+            session_cookie.unwrap()
+        );
+        // continue to decode the session cookie
+        let user_id = if let Some(session) = store
+            .load_session(session_cookie.unwrap().to_owned())
+            .await
+            .unwrap()
+        {
+            if let Some(user_id) = session.get::<UserId>("user_id") {
+                tracing::debug!(
+                    "UserIdFromSession: session decoded success, user_id={:?}",
+                    user_id
+                );
+                user_id
+            } else {
+                return Err((
+                    StatusCode::INTERNAL_SERVER_ERROR,
+                    "No `user_id` found in session",
+                ));
+            }
+        } else {
+            tracing::debug!(
+                "UserIdFromSession: err session not exists in store, {}={}",
+                crate::AXUM_SESSION_COOKIE_NAME,
+                session_cookie.unwrap()
+            );
+            return Err((StatusCode::BAD_REQUEST, "No session found for cookie"));
+        };
+
+        Ok(Self::FoundUserId(user_id))
+    }
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
+pub struct UserId(Uuid);
+
+impl UserId {
+    fn new() -> Self {
+        Self(Uuid::new_v4())
+    }
+}
diff --git a/src/kratos.rs b/src/kratos.rs
new file mode 100644
index 0000000..046c504
--- /dev/null
+++ b/src/kratos.rs
@@ -0,0 +1,633 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+
+// use axum::{
+// 	response::Html,
+// 	routing::get,
+// 	routing::post,
+// 	Router,
+// 	extract::{self,Extension,Path,Query},
+// 	http::{Request, header::HeaderMap, Method},
+//   body::{Bytes, Body},
+// };
+use std::collections::HashMap;
+use serde::{Deserialize}; //, Serialize};
+use reqwest::header::{
+	HeaderMap,
+	HeaderValue,
+	USER_AGENT, AUTHORIZATION, SET_COOKIE, COOKIE,	ACCEPT, CONTENT_TYPE
+};
+use reqwest::StatusCode;
+//use reqwest::cookie::Cookie;
+// use tower_cookies::{Cookie, Cookies};
+use anyhow::{anyhow,Result};
+use reqwest_cookie_store::CookieStoreMutex;
+use app_env::config::SigninServer; 
+use std::sync::Arc;
+
+const PASSWORD_FIELD: &str = "password";
+
+// use crate::defs::{DataDBs}; 
+// use crate::utils::reqenv::ReqEnv;
+/*
+fn parse_url(text: &str, lang: &str) -> reqwest::Url {
+    let mut url = reqwest::Url::parse("https://translate.google.com/translate_tts?ie=UTF-8&total=1&idx=0&client=tw-ob").unwrap();
+    url.query_pairs_mut()
+        .append_pair("tl", lang)
+        .append_pair("q", text)
+        .append_pair("textlen", &text.len().to_string())
+        .finish();
+    url
+}
+
+
+fn get_url_flow(base: String, flow: String, query: String ) -> String {
+	format!("{}/self-service/{}/browser{}",base,flow,query)
+}
+*/
+fn default_empty() -> String {
+  "/".to_string()
+}
+fn default_array_empty() -> Vec<String> {
+  Vec::new()
+}
+fn default_false() -> bool {
+	false
+}
+fn default_true() -> bool {
+	true
+}
+
+// TODO load Traits from json file  "identity.schema.json" .properties.traits 
+// What about additionalProperties
+pub type JsonMap = serde_json::Map<String, serde_json::Value>;
+
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosItemProps {
+	pub title: String,
+	#[serde(alias = "type")]
+	pub typ: String,
+}
+
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosTraitItem {
+	#[serde(alias = "type")]
+  pub typ: String,
+	pub properties: Option<HashMap::<String,KratosItemProps>>,
+} 
+
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosUser {
+	pub traits: HashMap::<String,KratosTraitItem>,
+	pub password: String,
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosMetaLabel {
+  id: String,
+  text: String,
+	#[serde(alias = "type")]
+  typ: String,
+  context: String, // {}
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosMeta {
+	label: KratosMetaLabel,
+}
+fn default_kratos_meta() -> KratosMeta {
+	KratosMeta::default()
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosAttrib {
+	name: String,
+	#[serde(alias = "type")]
+  typ: String,
+	#[serde(default = "default_empty")]
+  value: String,
+	#[serde(default = "default_false")]
+  required: bool,
+	#[serde(default = "default_true")]
+  disabled: bool,
+  node_type: String,
+	#[serde(default = "default_array_empty")]
+	messages: Vec<String>,
+	#[serde(default = "default_kratos_meta")]
+	meta: KratosMeta, 
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosNode {
+	#[serde(alias = "type")]
+	typ: String,
+	group: String,
+	attributes: KratosAttrib,
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosUi {
+  action: String,
+  method: String,
+  nodes: Vec<KratosNode>,
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosHeader {
+	#[serde(alias = "set-cookie")]
+  cookie: String,
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosIdentity {
+	pub created_at: String,
+	pub id: String,
+	// pub recovery_addresses: Vec<serde_json::Value>,
+	pub schema_id: String,
+	pub schema_url: String,
+	pub state: String,
+	pub state_changed_at: String,
+	//pub traits: Vec<serde_json::Value>,
+	pub updated_at: String,
+	//pub verifiable_addresses: Vec<serde_json::Value>,
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosSession {
+	pub id: String,
+	pub active: bool,
+	pub expires_at: String,
+	pub authenticated_at: String,
+	pub issued_at: String,
+	pub identity: KratosIdentity,
+	// pub authentication_methods: Vec<serde_json::Value>,
+}
+fn default_default_session() -> KratosSession {
+	KratosSession::default()
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosSessionResponse {
+	#[serde(default = "default_empty")]
+	pub session_token: String,
+	#[serde(default = "default_default_session")]
+	pub session: KratosSession,
+}
+
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosResponse {
+	id: String,
+	#[serde(alias = "type")]
+  typ: String,
+  expires_at: String,
+  issued_at: String,
+  request_url: String,
+  ui: KratosUi,
+}
+#[derive(Debug, Deserialize,Default)]
+#[allow(dead_code)]
+pub struct KratosReqId {
+	pub flow: String,
+	pub csrf: String,
+	pub cookie: String,
+}
+#[derive(Debug,Default)]
+#[allow(dead_code)]
+pub struct OnKratosReq {
+	pub status: StatusCode,
+	pub cookie: String,
+	pub session_resp: KratosSessionResponse,
+}
+
+fn load_idschema(path: String) -> JsonMap {
+  let str_data = std::fs::read_to_string(&path)
+    .unwrap_or_else(|e| {
+    	eprintln!("Error read idschema {}: {}",&path,e);
+   		 String::from("")
+  });
+  if str_data.is_empty() {
+		JsonMap::default()
+	} else {
+		serde_json::from_str(&str_data).unwrap_or_else(|e| {
+			eprintln!("Error loading idschema {}: {}",&path,e);
+		  JsonMap::default()
+		})
+	}
+}
+
+fn get_item_props(val: &serde_json::Value) -> HashMap::<String,KratosItemProps>  {
+  let mut props: HashMap::<String,KratosItemProps> = HashMap::new();
+  if let Some(data_obj)= val["properties"].as_object() { 
+		for (ky,val) in data_obj {
+			props.insert(ky.to_owned(), KratosItemProps {
+				title: format!("{}",val["title"]).replace('"',""),
+				typ: format!("{}",val["type"]).replace('"',""),
+			});
+		}
+	}
+	props
+}
+pub fn load_traits(path: String) -> HashMap::<String,KratosTraitItem> {
+	let idschema: JsonMap = load_idschema(path);
+	// dbg!("{:#?}",&idschema["properties"]["traits"]["properties"]);
+	let mut traits: HashMap::<String,KratosTraitItem> = HashMap::new();
+  if let Some(data_obj)= idschema["properties"]["traits"]["properties"].as_object() {
+		for (ky,val) in data_obj {
+			let value = format!("{}",val["type"]).replace('"',"");
+			match value.as_str() {
+				"object" => {
+					traits.insert(ky.to_owned(),KratosTraitItem {
+						typ: value,
+						properties: Some(get_item_props(val)),
+					});
+				},
+				"string" => {
+					traits.insert(ky.to_owned(),KratosTraitItem {
+						typ: value,
+						properties: None,
+					});
+				},
+				_ => {
+					println!("Type {} undefined",value.as_str());
+				},
+			}
+		}
+	};
+	traits
+}
+pub fn set_kratos_user(mode: &str, data: HashMap::<String,String>,traits:  HashMap::<String,KratosTraitItem>) -> String {
+  let mut user_data = String::from("{");
+	for (ky,def) in traits {
+		match def.typ.as_str() {
+			"object" => {
+	      if let Some(props) =  def.properties {
+	        if mode == "json" {
+						user_data += &format!("\"{}\": ",ky);
+					  user_data += &"{";
+					}
+					for (it_ky,_it_def) in props {
+						let key = format!("{}.{}",ky,it_ky);
+					  if let Some(value) = data.get(&key) {
+	            if mode == "json" {
+					      user_data += &format!("\"{}\": \"{}\",",it_ky,value);
+							} else {
+					      user_data += &format!("\"{}.{}.{}\": \"{}\",",mode,ky,it_ky,value);
+							}
+						}	 
+					}
+	        if mode == "json" {
+						user_data += &"}#,";
+						user_data = user_data.replace(",}#","}");
+					}
+				}
+			},
+			_ => { 
+				if let Some(value) = data.get(&ky) {
+	        if mode == "json" {
+					  user_data += &format!("\"{}\": \"{}\",",ky,value);
+					} else {
+					  user_data += &format!("\"{}.{}\": \"{}\",",mode,ky,value);
+					}
+				}
+			},
+		}
+	}
+	if let Some(passwd) = data.get(PASSWORD_FIELD) {
+		user_data += &format!("\"{}\":\"{}\",",PASSWORD_FIELD,passwd);
+		user_data += &format!("\"method\":\"{}\",",PASSWORD_FIELD);
+	}
+	user_data += &"}#";
+	user_data = user_data.replace(",}#","}");
+  user_data
+}
+pub fn set_kratos_login(data: HashMap::<String,String>) -> String {
+  let mut user_data = String::from("{");
+	for (ky,val) in data {
+		user_data += &format!("\"{}\": \"{}\",",ky,val);
+	}
+  user_data += &format!("\"method\":\"{}\",",PASSWORD_FIELD);
+	user_data += &"}#";
+	user_data = user_data.replace(",}#","}");
+  user_data
+}
+pub fn get_req_cli(cfg: SigninServer,cookie_store: &Arc<CookieStoreMutex>) -> Result<reqwest::Client> {
+//pub fn get_req_cli(cfg: SigninServer) -> Result<reqwest::Client> {
+	let mut req_cli = reqwest::Client::builder();
+	if cfg.conn_timeout > 0 {
+    req_cli = req_cli.connect_timeout(std::time::Duration::from_millis(cfg.conn_timeout));
+	} 
+	if cfg.timeout > 0 {
+    req_cli = req_cli.timeout(std::time::Duration::from_millis(cfg.timeout));
+	} 
+	//match req_cli.cookie_provider(std::sync::Arc::clone(cookie_store)).build() {
+	//match req_cli.cookie_store(true).build() {
+	match req_cli.build() {
+		Ok(reqcli) => Ok(reqcli),
+		Err(e) => Err(anyhow!("Error get req client builder {}", &e)), 
+	}
+}
+pub fn create_headers(id: &str,token: &str,csrf_token: &str, cookie: &str) -> Result<HeaderMap> {
+	let mut headers = HeaderMap::new();
+  headers.insert(USER_AGENT, HeaderValue::from_static("reqwest"));
+  headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
+  headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
+	// if cookie != "" {
+  //   headers.insert(COOKIE, HeaderValue::from_str(cookie)
+	// 		.unwrap_or(HeaderValue::from_static(""))
+	// 	);
+  //   // headers.insert(SET_COOKIE, HeaderValue::from_str(cookie)
+	// 	// 	.unwrap_or(HeaderValue::from_static(""))
+	// 	// );
+	// }
+	if token != "" {
+		headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}",token))
+			.unwrap_or(HeaderValue::from_static(""))
+		);
+	}
+	// if csrf_token != "" {
+  //   headers.insert("X-CSRF-Token", 
+	//     HeaderValue::from_str(csrf_token).unwrap_or(HeaderValue::from_static(""))
+	//   );
+	// }
+	if id != "" {
+    headers.insert("x-kratos-authenticated-identity-id", 
+	    HeaderValue::from_str(id).unwrap_or(HeaderValue::from_static(""))
+	  );
+	}
+	// dbg!("{:#?}",&headers);
+	// dbg!("{:#?}",&headers);
+	Ok(headers)
+}
+pub fn write_cookies(cfg: SigninServer,cookie_store: &Arc<CookieStoreMutex>) -> Result<()> {
+  let mut writer = std::fs::File::create(cfg.cookies_path.to_owned())
+      .map(std::io::BufWriter::new)?;
+  match cookie_store.lock() {
+		Ok(store) => {
+      store.save_json(&mut writer).unwrap_or_else(|e|{
+		    println!("Error save cookies to {}: {}",cfg.cookies_path,e);
+			});
+		},
+		Err(e) => { println!("Error save cookies: {}",e);},
+	};
+	Ok(())
+}
+pub fn get_root_url(protocol: String, base: String, port: usize) -> String {
+	format!("{}://{}:{}",protocol,base,port)
+}
+pub fn write_store_cookie(cfg: SigninServer, cookie_store: &Arc<CookieStoreMutex>) {
+ let _ = write_cookies(cfg.to_owned(), cookie_store);
+ match cookie_store.lock() {
+		Ok(store) => {
+			for c  in store.iter_any() {
+			  let json_data = serde_json::to_string(&c).unwrap();
+				println!("c: {:#?}", c);
+				println!("{}", json_data);
+			}
+		},
+		Err(e) => { println!("Error save cookies: {}",e);},
+	};
+}
+pub async fn get_flow_csrf(token: &str,cfg: SigninServer, cookie_store: &Arc<CookieStoreMutex>, csrf_token: &str, cookie: &str, url: &str) -> Result<KratosReqId> {
+// pub async fn get_flow_csrf(token: &str,cfg: SigninServer, csrf_token: &str, cookie: &str, url: &str) -> Result<KratosReqId> {
+	let reqid: KratosReqId;
+	let req_cli = get_req_cli(cfg.to_owned(), cookie_store)?;
+	let headers = create_headers("",token,csrf_token,cookie)?;
+  match req_cli
+	  .get(url)
+    // .bearer_auth(token.access_token().secret())
+		.headers(headers)
+    .send()
+    .await {
+			Ok(data) => {
+				let flow: String;
+				let csrf: String;
+				let cookie: String;
+				if let Some(ckie) = data.headers().get(SET_COOKIE) {
+				  let str_cookie = &format!("{:?}",&ckie).replace("\"","");
+					let (cookie_val,_) = str_cookie.split_once(";").unwrap_or_else(||("",""));
+					cookie=cookie_val.to_owned();
+				} else {	
+					cookie = String::from("");
+				}	
+	      //dbg!("{:#?}",&data);
+				if ! url.contains("logout") { // && data.status() == StatusCode::OK {
+					match data.json::<KratosResponse>().await {
+						Ok(response) => { 
+							//  dbg!("{:#?}",&response);
+							//let (_url_path,url_flow) = &response.ui.action.as_str().split_once("flow=").unwrap_or_else(||("",""));
+							//flow=url_flow.to_string();
+							flow=response.id;
+							if let Some(req_csrf_token) = response.ui.nodes.iter().filter(|n| n.attributes.name.as_str() == "csrf_token").collect::<Vec<&KratosNode>>().into_iter().nth(0) {
+								csrf=req_csrf_token.attributes.value.to_string();
+							} else {
+								csrf=String::from("");
+							}
+							reqid = KratosReqId { flow, csrf, cookie };
+						},
+						Err(e) => {
+							return Err(anyhow!("Error get flow json: {}", &e));
+						}
+					};
+				} else {
+					match data.json::<serde_json::Value>().await {
+						Ok(response) => { 
+							dbg!("{:#?}",&response);
+							//let (_url_path,url_flow) = &response.ui.action.as_str().split_once("flow=").unwrap_or_else(||("",""));
+							//flow=url_flow.to_string();
+							// flow=response.id;
+							// if let Some(req_csrf_token) = response.ui.nodes.iter().filter(|n| n.attributes.name.as_str() == "csrf_token").collect::<Vec<&KratosNode>>().into_iter().nth(0) {
+							// 	csrf=req_csrf_token.attributes.value.to_string();
+							// } else {
+							// 	csrf=String::from("");
+							// }
+							reqid = KratosReqId { flow: String::from(""), csrf: String::from(""), cookie: String::from("") };
+						},
+						Err(e) => {
+							return Err(anyhow!("Error get flow json: {}", &e));
+						}
+					};
+				}
+			},
+			Err(e) => {
+				return Err(anyhow!("Error get flow {}", &e));
+			},
+	};
+	Ok(reqid)
+}
+pub async fn on_kratos_user(token: &str,cfg: SigninServer, cookie_store: &Arc<CookieStoreMutex>, csrf_token: &str, cookie: &str, url: &str, data: String,) -> Result<OnKratosReq> {
+// pub async fn on_kratos_user(token: &str, cfg: SigninServer, csrf_token: &str, cookie: &str,url: &str, data: String,) -> Result<OnKratosReq> {
+	// let mut body_data = String::from("{");
+	// // body_data += &format!("\"csrf_token\":\"{}\",#{}",csrf_token,data);
+	// body_data += &format!("\"csrf_token\":\"{}\",#{}",csrf_token,data);
+	// body_data = body_data.replace("#{","");
+	let  body_data = String::from(data);
+	println!("{}",&body_data);
+	let json_data: serde_json::Value = serde_json::from_str(&body_data).unwrap_or_else(|e|{
+    println!("Error json: {}",e);
+		serde_json::Value::default()
+	});
+	let req_cli = get_req_cli(cfg.to_owned(), cookie_store)?;
+	let headers = create_headers("",token,csrf_token,cookie)?;
+	println!("URL: {}",&url);
+	let status: StatusCode;
+	let cookie: String;
+	let session_resp: KratosSessionResponse;
+	match req_cli
+		.post(url)
+		.headers(headers)
+		.json(&json_data)
+		.send()
+		.await {
+			Ok(res) => {
+				if let Some(ckie) = res.headers().get(SET_COOKIE) {
+				  let str_cookie = &format!("{:?}",&ckie).replace("\"","");
+					dbg!("{:#?}",&ckie);
+					let (cookie_val,_) = str_cookie.split_once(";").unwrap_or_else(||("",""));
+					cookie=cookie_val.to_owned();
+				} else {	
+					cookie = String::from("");
+				}
+				status = res.status();
+				if url.contains("login") || url.contains("whoami") {
+				  // match res.json::<serde_json::Value>().await {
+				  match res.json::<KratosSessionResponse>().await {
+						Ok(data) => {
+							// dbg!("{:#?}",&data);
+					    // session_resp = KratosSessionResponse::default();
+							session_resp = data;
+						},
+						Err(e) => {
+							return Err(anyhow!("Error on kratos response session json  {}", &e));
+						}
+					}
+				} else {
+					session_resp = KratosSessionResponse::default();
+				}
+			},
+			Err(e) => {
+				println!("Error get :{}",e);
+				return Err(anyhow!("Error on kratos  {}", &StatusCode::BAD_REQUEST));
+			},
+		// match response.status() {
+		// reqwest::StatusCode::OK => {
+		//     println!("Success! {:?}");
+		// },
+		// reqwest::StatusCode::UNAUTHORIZED => {
+		//     println!("Need to grab a new token");
+		// },
+		// _ => {
+		//     panic!("Uh oh! Something unexpected happened.");
+		// },
+	};
+	Ok(OnKratosReq { status,cookie,session_resp })
+}
+pub async fn get_kratos_user(id: &str,token: &str,cfg: SigninServer,cookie_store: &Arc<CookieStoreMutex>, csrf_token: &str, cookie: &str, url: &str) -> Result<OnKratosReq> {
+//pub async fn get_kratos_user(id: &str,token: &str,cfg: SigninServer, csrf_token: &str, cookie: &str, url: &str) -> Result<OnKratosReq> {
+	// let reqid: KratosReqId;
+	let req_cli = get_req_cli(cfg.to_owned(), cookie_store)?;
+	let headers = create_headers(id,token,csrf_token,cookie)?;
+	// dbg!("{:#?}",&headers);
+	let status: StatusCode;
+	let cookie = String::from("");
+	let session_resp: KratosSessionResponse;
+  match req_cli
+	  .get(url)
+		.headers(headers)
+    .send()
+    .await {
+			Ok(res) => {
+	      // dbg!("{:#?}",&res);
+				status = res.status();
+				match res.json::<serde_json::Value>().await {
+				 //match res.json::<KratosSessionResponse>().await {
+					Ok(data) => {
+						// dbg!("{:#?}",&data);
+				    session_resp = KratosSessionResponse::default();
+				// if url.contains("logout") {
+				// 	session_resp = KratosSessionResponse::default();
+				// } else {
+				// 	match res.json::<KratosSession>().await {
+				// 		Ok(session) =>  session_resp = KratosSessionResponse {
+				// 			session_token: token.to_owned(),
+				// 			session,
+					},
+					Err(e) => {
+						return Err(anyhow!("Error get kratos session json  {}", &e));
+					}
+				}
+			},
+			Err(e) => {
+				println!("Error get :{}",e);
+				return Err(anyhow!("Error get kratos user  {}", &StatusCode::BAD_REQUEST));
+			},
+	};
+	Ok(OnKratosReq { status,cookie,session_resp })
+}
+pub async fn logout_kratos_user(id: &str,token: &str,cfg: SigninServer, cookie_store: &Arc<CookieStoreMutex>, csrf_token: &str, cookie: &str, url: &str) -> Result<OnKratosReq> {
+	// let reqid: KratosReqId;
+	let mut body_data = String::from("{");
+	body_data += &format!("\"session_token\":\"{}\"",&token);
+	// body_data += &format!("\"ory_kratos_session\":\"{}\"",&token);
+	body_data += "}";
+	println!("{}",&body_data);
+	let json_data: serde_json::Value = serde_json::from_str(&body_data).unwrap_or_else(|e|{
+    println!("Error json: {}",e);
+		serde_json::Value::default()
+	});
+	let req_cli = get_req_cli(cfg.to_owned(), cookie_store)?;
+	let headers = create_headers(id,token,csrf_token,cookie)?;
+	// dbg!("{:#?}",&headers);
+	let status: StatusCode;
+	let cookie = String::from("");
+	let session_resp: KratosSessionResponse;
+  match req_cli
+	  .delete(url)
+	//  .get(url)
+		.headers(headers)
+		.json(&json_data)
+    .send()
+    .await {
+			Ok(res) => {
+	      dbg!("{:#?}",&res);
+				status = res.status();
+				if status == StatusCode::OK {
+					match res.json::<serde_json::Value>().await {
+					//match res.json::<KratosSessionResponse>().await {
+						Ok(data) => {
+							dbg!("{:#?}",&data);
+							session_resp = KratosSessionResponse::default();
+						},
+						Err(e) => {
+							return Err(anyhow!("Error parse logout kratos session json  {}", &e));
+						},
+					}
+				} else {
+					session_resp = KratosSessionResponse::default();
+		   	}
+//				if url.contains("logout") {
+				// } else {
+				// 	match res.json::<KratosSession>().await {
+				// 		Ok(session) =>  session_resp = KratosSessionResponse {
+				// 			session_token: token.to_owned(),
+				// 			session,
+				// 		},
+				// 		Err(e) => {
+				// 			return Err(anyhow!("Error get kratos session json  {}", &e));
+				// 		}
+				// 	};
+				// }
+			},
+			Err(e) => {
+				println!("Error get :{}",e);
+				return Err(anyhow!("Error logout kratos user  {}", &StatusCode::BAD_REQUEST));
+			},
+	};
+	Ok(OnKratosReq { status,cookie,session_resp })
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..b0feed9
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,413 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+
+//! Run with
+//!
+//! ```not_rust
+//! cargo run -p example-static-file-server
+//! ```
+//! https://rustrepo.com/repo/TrueLayer-reqwest-middleware
+//! https://github.com/AscendingCreations/AxumCSRF
+//! 
+
+use std::{
+	sync::Arc,
+	time::Duration,
+};
+use tokio::sync::Mutex;
+
+use axum::{ 
+	error_handling::HandleErrorLayer,
+	http::{StatusCode, header::HeaderValue, Method},
+	routing::get_service,
+	AddExtensionLayer,
+  Router,
+};
+use axum_server::tls_rustls::RustlsConfig;
+use tower_http::{
+	services::ServeDir,
+	trace::TraceLayer,
+  cors::{CorsLayer, Origin},
+};
+use tower::{ServiceBuilder};
+
+use app_env::{
+  AppStore,
+  appenv::AppEnv,
+  appinfo::AppInfo,
+  appdata::AppData,
+  config::{Config}
+};
+use connectors::defs::{AppDataConn}; 
+use app_auth::AuthStore;
+use anyhow::{Result};
+
+//use crate::defs::{DataDBs,CollsData};
+use crate::defs::{DataDBs};
+
+// static WEBSERVER: AtomicUsize = AtomicUsize::new(0);
+const PKG_VERSION: &'static str = env!("CARGO_PKG_VERSION");
+const APP_NAME: &'static str = "libresignin";
+// const PKG_VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
+const PKG_NAME: &'static str = env!("CARGO_PKG_NAME");
+const PKG_AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");
+const PKG_DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION");
+
+const AXUM_SESSION_COOKIE_NAME: &str = "axum_session";
+
+pub mod defs;
+pub mod kratos;
+pub mod handlers;
+pub mod utils;
+pub mod state;
+
+pub type BxDynResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
+
+async fn create_auth_store(app_env: &AppEnv,verbose: isize) -> AuthStore {
+	let config = app_env.get_curr_websrvr_config();
+	let model_path = config.st_auth_model_path();
+  let policy_path = config.st_auth_policy_path();
+  AuthStore::new(&config,AuthStore::create_enforcer(model_path,policy_path).await,verbose)
+}
+async fn up_web_server(webpos: usize) -> Result<()> { // WebSettings> {
+	let debug = envmnt::get_isize("DEBUG",0);
+	let verbose = envmnt::get_isize("WEB_SERVER_VERBOSE", 0);
+	let mut app_env = AppEnv::default();
+	app_env.curr_web=webpos;
+	if verbose > 0 {
+		println!("Web services: init {} ___________ ", chrono::Utc::now().timestamp());
+	}
+	app_env.info = AppInfo::new(
+		APP_NAME,
+		format!("web: {}",&webpos),
+		format!("version: {}",PKG_VERSION),
+		format!("authors: {}",PKG_AUTHORS),
+		format!("{}",PKG_DESCRIPTION),
+	);
+	webenv::init_app(&mut app_env,verbose).await.unwrap_or_else(|e| 
+		panic!("Error loadding app environment {}",e)
+	);
+	let config = app_env.get_curr_websrvr_config();
+	// let webserver_status = WEBSERVER.load(Ordering::Relaxed);
+	let zterton_env = envmnt::get_or(format!("_{}",&config.name).as_str(), "UNKNOWN");
+	// if webserver_status != 0 {
+	if zterton_env != "UNKNOWN" {
+		if verbose  > 0 {
+			println!("{} web services at {}",APP_NAME,&zterton_env);
+		}
+		return Ok(()); 
+	}
+	// WEBSERVER.store(1,Ordering::Relaxed);
+  // TODO pass root file-name frmt from AppEnv Config 
+	if verbose > 0 {
+		println!("Loading webserver: {} ({})",&config.name,&app_env.curr_web);
+	}	
+  let (app, socket) = webenv::start_web(&mut app_env).await;
+	if verbose > 0 {
+ 	 println!("Load app store ...");
+	}
+  // `MemoryStore` just used as an example. Don't use this in production.
+  let mem_store = async_session::MemoryStore::new();
+
+  let app_store = AppStore::new(AppData::new(app_env.to_owned(),verbose));
+  // As static casbin
+	if verbose > 0 {
+  	println!("Load auth store ...");
+	}
+	let auth_store = create_auth_store(&app_env,verbose).await;
+	if verbose > 0 {
+  	println!("Load data store ...");
+	}
+	let app_data_conn = AppDataConn::new(APP_NAME.to_string(),app_env.config.datastores_settings.to_owned(),"").await;
+	let cookie_store = {
+    let file = std::fs::File::open(&config.signin.cookies_path)
+      .map(std::io::BufReader::new)
+			.unwrap_or_else(|e| 
+		    panic!("Error creating cookie path {}: {}",config.signin.cookies_path,e)
+			);
+			cookie_store::CookieStore::load_json(file)
+			.unwrap_or_else(|e| 
+		    panic!("Error unable to load path {}: {}",config.signin.cookies_path,e)
+			)
+  };
+  let cookie_store = reqwest_cookie_store::CookieStoreMutex::new(cookie_store); 
+	let cookie_store = std::sync::Arc::new(cookie_store);
+	let req_cli = reqwest::Client::builder()
+//   .timeout(std::time::Duration::from_millis(500))
+//	  .connect_timeout(std::time::Duration::from_millis(100))
+		.cookie_store(true)
+    .build().unwrap_or_else(|e| 
+		  panic!("Error creating reqwest client: {}",e)
+	);
+  let data_dbs = DataDBs {
+    // colls: CollsData::new(app_env.to_owned(),verbose),
+    app: app_store.to_owned(),
+    auth: auth_store.to_owned(),
+		conns: Arc::new(Mutex::from(app_data_conn)),
+  };
+	if verbose > 0 {
+ 	 println!("Load web filters ...");
+	}
+
+  // let us get some static boxes from config values: 
+  let log_name = app_env.config.st_log_name();
+  // Path for static files 
+  let html_path: &str;
+	if config.html_path.is_empty() {
+    html_path = "/";
+	} else {
+    html_path = config.st_html_path();
+	}
+  let html_url: String;
+	if config.html_url.is_empty() {
+    html_url = String::from("/");
+	} else {
+    html_url = config.html_url.to_owned();
+	}
+  // // If not graphQL comment/remove next line 
+  // let gql_path = config.st_gql_req_path();
+  // // If not graphiQL comment/remove next line Interface GiQL
+  // let giql_path = config.st_giql_req_path();
+
+  // let origins: < http::HeaderValue> = config.allow_origin.iter().map(AsRef::as_ref).collect();
+  let mut origins: Vec<HeaderValue> = Vec::new();
+	for itm in config.allow_origin.to_owned() {
+		match HeaderValue::from_str(itm.as_str()) {
+     Ok(val) => origins.push(val),
+		 Err(e) =>  println!("error {} with {} header for allow_origin",e,itm),
+		}
+	}	
+/*
+  let cors = warp::cors()
+    //.allow_any_origin()
+    .allow_origins(origins)
+    //.allow_origins(vec![app_env.config.allow_origin.as_str(), "https://localhost:8000"])
+    .allow_credentials(true)
+    .allow_header("content-type")
+    .allow_header("Authorization")
+    .allow_methods(&[Method::GET, Method::POST, Method::DELETE]);
+
+  let auth_api = 
+  // Auth routes for login & logout  REQUIRED
+  app_auth_filters::auth(app_store.clone(),auth_store.clone(),cors.clone()); // .with(cors.clone());
+  
+  let gqli_api = 
+  // If not graphiQL comment/remove next line Interface GiQL MUST BEFORE graphql post with schema
+  // app_api.to_owned()
+  graphql::graphiql(gql_path, giql_path, data_dbs.clone()).await;
+  if giql_path.len() > 0 && verbose > 0 {
+    println!(
+     "GraphiQL url: {}://{}:{}/{}",
+     &app.protocol, &app.host, &app.port, &giql_path
+    );
+  } 
+	let mut cloud = Cloud::default();
+	env_cloud("*", &mut cloud.env).await?;
+	load_cloud_env(&mut cloud).await;	
+  // If not graphQL comment/remove next line 
+  let gql_api=graphql::graphql(gql_path, data_dbs.clone(),cors.clone()).await; //.with(cors.clone());
+  // // Add ALL ENTITIES to work with here
+  let kloud_api = filters::CollFilters::new(&config.prefix)
+		.filters_config(data_dbs.clone(),cloud.clone(),cors.clone());
+
+  let file_api = app_file_filters::files(app_store.clone(),auth_store.clone()).with(cors.clone());
+  // Path for static files, better to be LAST
+  let fs_api = warp::fs::dir(html_path).with(warp::compression::gzip());
+
+	let home_api = filters::CollFilters::new(&config.prefix)
+	  .filters_home(data_dbs.clone(),cloud.clone(),cors.clone(),"info");
+
+  let app_api = auth_api
+			.or(gqli_api).or(gql_api)
+			.or(home_api)
+			.or(kloud_api)
+			.or(file_api)
+			.or(fs_api)
+			.recover(move | error: warp::Rejection| handle_rejection(error, app_store.clone()))
+			.boxed();
+  // Wrap routes with log to get info
+  let routes =  app_api.with(warp::log(log_name));
+  //let routes = app_api.with(cors).with(warp::log(log_name));
+*/
+  println!(
+    "Starting http server: {}://{}:{}",
+    &app.protocol, &app.host, &app.port
+  );
+	envmnt::set(APP_NAME, format!("{}:{}",&app.host,&app.port));
+	if debug > 0 {
+ 	  println!("Web services: done {} __________ ",chrono::Utc::now().timestamp());
+	}
+	let mut web_router = Router::new();
+	web_router = crate::handlers::router::router_handlers(web_router);
+	if !config.html_path.is_empty() {
+		web_router = web_router.nest(
+				html_url.as_str(),
+				get_service(ServeDir::new(html_path)).handle_error(|error: std::io::Error| async move {
+						(
+								StatusCode::INTERNAL_SERVER_ERROR,
+								format!("Unhandled internal error: {}", error),
+						)
+				}),
+		);
+	}
+	if config.allow_origin.len() > 0 {
+	  web_router = web_router.layer(CorsLayer::new()
+		  .allow_origin(Origin::list(origins))
+			.allow_methods(vec![Method::GET, Method::POST])
+		);
+	}
+  web_router = web_router.layer(AddExtensionLayer::new(mem_store));
+	web_router = web_router.layer(AddExtensionLayer::new(data_dbs));
+	web_router = web_router.layer(AddExtensionLayer::new(cookie_store));
+	web_router = web_router.layer(TraceLayer::new_for_http());
+	web_router = web_router.layer(tower_cookies::CookieManagerLayer::new());
+	web_router = web_router.layer(
+		 ServiceBuilder::new()
+				// Handle errors from middleware
+				.layer(HandleErrorLayer::new(handlers::router::handle_error))
+				.load_shed()
+				.concurrency_limit(1024)
+				.timeout(Duration::from_secs(10))
+				.layer(TraceLayer::new_for_http())
+				.layer(AddExtensionLayer::new(crate::state::SharedState::default()))
+				.into_inner(),
+	);
+	if app.protocol.clone().as_str() == "http" {
+		let _ = axum::Server::bind(&socket)
+			.serve(web_router.into_make_service())
+			.await
+			.expect("server failed");
+    // let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
+    // tracing_subscriber::fmt::init();
+    // tracing::debug!("listening on {}", addr);
+    //let _ = axum::Server::bind(&addr)
+    // let _ = axum::Server::bind(&socket)
+    //  .serve(app.into_make_service())
+    //   .await;
+    //  .unwrap();
+	} else {
+		match RustlsConfig::from_pem_file(
+		  format!("{}/ssl/{}", &config.resources_path, "fullchain.pem")
+		  ,format!("{}/ssl/{}", &config.resources_path,"privkey.pem")
+		).await {
+      Ok(ssl_cfg) => {
+				let _ = axum_server::bind_rustls(socket,ssl_cfg)
+					.serve(web_router.into_make_service())
+					.await
+					.expect("server failed");
+			},
+			Err(e) => {
+				println!("SSL Certifcates error: {}",e);
+				return Ok(());
+			}
+		};
+	}
+	Ok(())
+}
+// fn get_args() -> (String,String) {
+// 	let args: Vec<String> = std::env::args().collect();
+// 	let mut arg_cfg_path = String::from("");
+// 	let mut arg_env_path = String::from("");
+// 	args.iter().enumerate().for_each(|(idx,arg)| {
+// 		if arg == "-c" { 
+// 			arg_cfg_path=args[idx+1].to_owned();
+// 		} else if arg == "-e" { 
+// 			arg_env_path=args[idx+1].to_owned();
+// 		}
+//   });
+// 	(arg_cfg_path,arg_env_path)
+// }
+//async fn get_app_env(arg_cfg_path: String,verbose: isize) -> Result<(Cloud,AppEnv)> {
+//	let mut cloud = Cloud::default();
+//	load_cloud_env(&mut cloud).await;	
+// async fn get_app_env(arg_cfg_path: String,verbose: isize) -> Result<AppEnv> {
+// 	let mut app_env = AppEnv::default();
+// 	let config_content = Config::load_file_content(verbose,&arg_cfg_path);
+//   if ! config_content.contains("run_mode") {
+// 		Err(anyhow!("Run mode not found in config {}", &arg_cfg_path))
+// 	} else {
+// 		app_env.config = Config::new(config_content,verbose);
+// 		// Ok((cloud,app_env))
+// 		Ok(app_env)
+// 	}
+// }
+/*
+async fn set_reqenv(app_env: &AppEnv,verbose: isize) -> ReqEnv {
+	let app_store =	AppStore::new(AppData::new(app_env.to_owned(),verbose));
+	let auth_store = create_auth_store(&app_env,verbose).await;
+	let mut headers = HeaderMap::new();
+  headers.insert(http::header::HOST, "localhost".parse().unwrap());
+	ReqEnv::new( 
+		app_store, 
+		auth_store,
+		headers,
+	  Method::GET,
+		"/config", "config", "kloud"
+	)
+}
+*/
+#[tokio::main]
+async fn main() -> BxDynResult<()> {  //std::io::Result<()> { 
+	let args: Vec<String> = std::env::args().collect();
+	if args.len() > 1 {
+		match args[1].as_str() {
+			"-h" | "--help" => {
+	  		println!("{} USAGE: -c config-toml -e env.file",PKG_NAME);
+				return Ok(());
+			},
+			"-v" | "--version" => {
+	  		println!("{} version: {}",PKG_NAME,PKG_VERSION);
+				return Ok(());
+			},
+			_ => println!("{}",PKG_NAME),
+		}
+	}
+	let debug=envmnt::get_isize("DEBUG",0);
+	let mut arg_cfg_path = String::from("");
+	let mut arg_env_path = String::from("");
+	args.iter().enumerate().for_each(|(idx,arg)| {
+		if arg == "-c" { 
+			arg_cfg_path=args[idx+1].to_owned();
+		} else if arg == "-e" { 
+			arg_env_path=args[idx+1].to_owned();
+		}
+  });
+	if !arg_env_path.is_empty() {
+		let env_path = std::path::Path::new(&arg_env_path);
+		dotenv::from_path(env_path)?;
+	}
+	pretty_env_logger::init();
+	let config_content = Config::load_file_content(debug, &arg_cfg_path);
+	if !config_content.contains("run_mode") {
+		panic!("Error no run_mode found or config path incomplete");
+	}
+	let config = Config::new(config_content,debug);
+	let app_data_conn = AppDataConn::new(APP_NAME.to_string(),config.datastores_settings.to_owned(),"").await;
+	if config.datastores_settings.len() > 0 {
+		if !app_data_conn.check_connections(config.datastores_settings.to_owned()).await {
+			println!("Error checking app data store connections");
+		} 
+  }
+	// Set the RUST_LOG, if it hasn't been explicitly defined
+	if std::env::var_os("RUST_LOG").is_none() {
+			std::env::set_var(
+					"RUST_LOG",
+					"example_static_file_server=debug,tower_http=debug",
+			)
+	}
+	if config.run_websrvrs {
+		for (pos,it) in config.websrvrs.iter().enumerate() {
+			if debug > 1 {
+				println!("{} -> {}",it.name,pos);
+			}
+			//tokio::join!(async move {
+			let handle = tokio::spawn(async move { up_web_server(pos).await });
+			let _ = handle.await;
+			tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
+		}
+	}
+   Ok(())
+}
diff --git a/src/state.rs b/src/state.rs
new file mode 100644
index 0000000..60ce555
--- /dev/null
+++ b/src/state.rs
@@ -0,0 +1,66 @@
+use axum::{
+    body::Bytes,
+    extract::{ContentLengthLimit, Extension, Path},
+    handler::Handler,
+    http::StatusCode,
+    routing::{get},
+    Router,
+};
+use std::{
+    collections::HashMap,
+    sync::{Arc, RwLock},
+};
+use tower_http::{
+   compression::CompressionLayer,// trace::TraceLayer,
+};
+
+pub type SharedState = Arc<RwLock<State>>;
+
+#[derive(Default)]
+pub struct State {
+  pub db: HashMap<String, Bytes>,
+}
+
+async fn kv_get(
+    Path(key): Path<String>,
+    Extension(state): Extension<SharedState>,
+) -> Result<Bytes, StatusCode> {
+    let db = &state.read().unwrap().db;
+
+    if let Some(value) = db.get(&key) {
+        Ok(value.clone())
+    } else {
+        Err(StatusCode::NOT_FOUND)
+    }
+}
+
+async fn kv_set(
+    Path(key): Path<String>,
+    ContentLengthLimit(bytes): ContentLengthLimit<Bytes, { 1024 * 5_000 }>, // ~5mb
+    Extension(state): Extension<SharedState>,
+) {
+    state.write().unwrap().db.insert(key, bytes);
+}
+
+async fn list_keys(Extension(state): Extension<SharedState>) -> String {
+    let db = &state.read().unwrap().db;
+
+    db.keys()
+        .map(|key| key.to_string())
+        .collect::<Vec<String>>()
+        .join("\n")
+}
+
+pub fn router_handlers(web_router: Router) -> Router {
+	let mut webrouter = web_router.to_owned();
+	webrouter = crate::handlers::kratos::def_handlers(webrouter);
+  webrouter
+    .route(
+				"/:key",
+				// Add compression to `kv_get`
+				get(kv_get.layer(CompressionLayer::new()))
+						// But don't compress `kv_set`
+						.post(kv_set),
+		)
+		.route("/keys", get(list_keys))
+}
\ No newline at end of file
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..88a2b02
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,7 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+pub mod reqtasks;
+pub mod reqenv;
\ No newline at end of file
diff --git a/src/utils/reqenv.rs b/src/utils/reqenv.rs
new file mode 100644
index 0000000..d50c6d6
--- /dev/null
+++ b/src/utils/reqenv.rs
@@ -0,0 +1,141 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+//use std::collections::HashMap;
+use std::fmt;
+//use std::str::from_utf8;
+//use tera::Tera;
+
+use axum::{
+	// async_trait,
+	// extract::{Extension, FromRequest, RequestParts, TypedHeader},
+  // extract::{Extension,Path,Query},
+	// headers::Cookie,
+	http::{
+//			self,
+			header::{HeaderMap, HeaderValue},
+//			Method,
+//			StatusCode,
+	},
+//	response::IntoResponse,
+};
+use crate::utils::reqtasks::ReqTasks;
+use app_env::{
+  appenv::AppEnv,
+  config::{Config, WebServer},
+  module::Module,
+  AppStore,
+    // AppData,
+};
+use app_auth::{
+  AuthStore,
+  User,
+  UserCtx,
+  LoginRequest,
+  // BEARER_PREFIX,
+  // AuthError,
+};
+
+/// `ReqEnv` includes ReqTasks as core type
+/// it is a kind of wrapping type 
+/// to declare:
+/// - auth methods locally 
+/// - other attributes
+/// - other request tasks methods 
+/// 
+#[derive(Clone)]
+pub struct ReqEnv {
+ pub req: ReqTasks,
+
+}
+
+impl fmt::Display for ReqEnv {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+   write!(f, "{} {} {}", &self.req.path, &self.req.origin, &self.req.key_module)
+  }
+}
+
+impl ReqEnv {
+  pub fn new(
+    app_db: AppStore,
+    auth_store: AuthStore,
+    header: HeaderMap<HeaderValue>,
+    method: &str,
+    path: &str,
+    origin: &str,
+    key_module: &str
+  ) -> Self {
+    let app_data = app_db.app_data.read();
+    // let auth_store: &'a AuthStore = &AuthStore {
+    //   users: auth_db.users.clone(),
+    //   sessions: auth_db.sessions.clone(),
+    //   enforcer: auth_db.enforcer.clone(),
+    // };
+    Self {
+      req: ReqTasks {
+        app_data: app_data.to_owned(),
+        auth_store: auth_store.to_owned(),
+        header,
+        method: method.to_string(),
+        path: format!("{}{}",key_module,path).to_string(),
+        origin: format!("{}{}",key_module,origin).to_string(),
+        key_module: key_module.to_string(),
+      },
+    }
+  }
+  /// Get `AppEnv`
+  #[must_use]
+  pub fn env(&self) -> AppEnv {
+    self.req.env()
+  }
+  /// Get Tera
+  #[must_use]
+  pub fn tera(&self) -> tera::Tera {
+    self.req.tera()
+  }
+  /// Get Context (ctx)
+  #[must_use]
+  pub fn ctx(&self) -> tera::Context {
+    self.req.ctx()
+  }
+  /// Get `AppEnv` Config
+  #[must_use]
+  pub fn config(&self) -> Config {
+    self.req.config()
+  }
+	#[must_use]
+  pub fn websrvr(&self) -> WebServer {
+    self.req.config().websrvrs[self.req.env().curr_web].to_owned()
+  }
+	#[must_use]
+  pub fn websrvr_url(&self) -> String {
+    let config=self.req.config().websrvrs[self.req.env().curr_web].to_owned();
+	  format!("{}://{}:{}", config.srv_protocol, config.srv_host, config.srv_port)
+  }
+  #[must_use]
+  pub fn module(&self) -> Module {
+    self.req.module()
+  }
+  #[must_use]
+  pub fn lang(&self) -> String {
+    self.req.lang()
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub fn token_from_header(&self) -> anyhow::Result<String> {
+    self.req.token_from_header()
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub async fn token_session(&self, login: &LoginRequest) -> anyhow::Result<String> {
+    self.req.token_session(login).await
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub async fn user_authentication(&self) -> anyhow::Result<UserCtx> {
+    self.req.user_authentication().await
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub async fn get_user(&self) -> User {
+    self.req.get_user().await
+  }
+}
\ No newline at end of file
diff --git a/src/utils/reqtasks.rs b/src/utils/reqtasks.rs
new file mode 100644
index 0000000..55a4b85
--- /dev/null
+++ b/src/utils/reqtasks.rs
@@ -0,0 +1,453 @@
+//
+/*! libresignin
+*/
+// Copyright 2022, Jesús Pérez Lorenzo
+//
+
+#![allow(clippy::needless_lifetimes)]
+
+use axum::{
+	// async_trait,
+	// extract::{Extension, FromRequest, RequestParts, TypedHeader},
+  // extract::{Extension,Path,Query},
+	// headers::Cookie,
+	http::{
+//			self,
+			header::{HeaderMap}, //, HeaderValue},
+//			Method,
+//			StatusCode,
+	},
+//	response::IntoResponse,
+};
+
+use std::collections::HashMap;
+use std::fmt;
+use std::str::from_utf8;
+
+use casbin::prelude::*;
+use tera::Tera;
+
+use app_env::{
+  AppStore,
+  appenv::AppEnv,
+  config::{Config,WebServer},
+  module::Module,
+  appdata::AppData
+};
+use app_tools::{hash_from_data,read_path_file};
+use app_auth::{
+  AuthStore,
+	User,
+  UserCtx,
+  BEARER_PREFIX,
+  AuthError,
+  LoginRequest,
+};
+
+// use salvo::{Request};
+
+use app_errors::AppError;
+
+use std::result::Result;
+// use std::io::Result;
+
+/// `ReqTasks` is a facilitator container for http request parsing and response composition
+/// Manage info from `AppData`  from env, vault, tera, ctx, etc.
+/// Evaluate token, session state
+/// Control some authorizations and permissions
+#[derive(Clone)]
+pub struct ReqTasks {
+  /// AppData object
+  pub app_data: AppData,
+//  pub app_data: &'a AppData,
+  /// Auth object
+  pub auth_store: AuthStore,
+//  pub auth_store: &'a AuthStore,
+  /// Request objet
+  pub header: HeaderMap,
+  pub method: String,
+  pub path: String,
+  /// It will allow to take decisions based in origin or source context.
+  pub origin: String,
+  pub key_module: String,
+}
+
+impl fmt::Display for ReqTasks {
+  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+   write!(f, "{} {} {}", &self.path, &self.origin, &self.key_module)
+  }
+}
+
+impl ReqTasks {
+  #[must_use]
+  pub fn new(
+    app_db: AppStore,
+    auth_store: AuthStore,
+    header: HeaderMap,
+    method: &str,
+    path: &str,
+    origin: &str,
+    key_module: &str
+  ) -> ReqTasks {
+    let app_data = app_db.app_data.read();
+    // let auth_store: &'a AuthStore = &AuthStore {
+    //   users: auth_db.users.clone(),
+    //   sessions: auth_db.sessions.clone(),
+    //   enforcer: auth_db.enforcer.clone(),
+    // };
+    ReqTasks {
+      app_data: app_data.to_owned(),
+      auth_store: auth_store.to_owned(),
+      header,
+      method: method.to_string(),
+      path: path.to_string(),
+      origin: origin.to_string(),
+      key_module: key_module.to_string(),
+    }
+  }
+  /// Get `AppEnv`
+  #[must_use]
+  pub fn env(&self) -> AppEnv {
+    self.app_data.env.to_owned()
+    // self.app_data.env.lock().await.to_owned()
+  }
+  /// Get Tera
+  #[must_use]
+  pub fn tera(&self) -> tera::Tera {
+    self.app_data.tera.to_owned()
+  }
+  // /// Get Vault
+  // pub fn vault(&self) -> ServerVault {
+  //   self.app_data.vault.to_owned()
+  // }
+  /// Get Context (ctx)
+  #[must_use]
+  pub fn ctx(&self) -> tera::Context {
+    self.app_data.ctx.to_owned()
+  }
+  /// Get `AppEnv` Config
+  #[must_use]
+  pub fn config(&self) -> Config {
+    self.app_data.env.config.to_owned()
+  }
+  /// Get `AppEnv` Config
+  #[must_use]
+  pub fn websrvr(&self) -> WebServer {
+    self.app_data.env.config.websrvrs[self.app_data.env.curr_web].to_owned()
+  }
+  #[must_use]
+  pub fn module(&self) -> Module {
+    self.app_data.env.get_module(&self.key_module)
+  }
+  /// Get `AppEnv` Config
+  // #[must_use]
+  // pub fn params(&self) -> HashMap<String,String> {
+  //   self.req
+  //   .uri()
+  //   .query()
+  //   .map_or_else(HashMap::new, |v| {
+  //       url::form_urlencoded::parse(v.as_bytes())
+  //           .into_owned()
+  //           .collect()
+  //   })
+  // }
+  /// Get Lang list from header - accept-language 
+  /// get first one but only root part as first two characters (es in case of es-ES) 
+  /// if lang is not in `WebServer.langs`it will fallback to `Config.default_lang`
+  #[must_use]
+  pub fn lang(&self) -> String {
+    #[allow(unused_assignments)]
+    let mut lang = self.config().default_lang;
+    let default_lang = self.config().default_lang;
+    let langs = self.websrvr().langs;
+    // As fallback set default_lang 
+    lang = default_lang;
+    // Get langs list from header
+    if let Some(languages) = self.header.get("accept-language") {
+      match languages.to_str() {
+        Ok(list_lngs) => {
+          // Get only first one
+          if let Some(full_lng) = list_lngs.split(',').collect::<Vec<_>>().first() {
+            // Get root_lng part 
+            let mut end: usize = 0;
+            full_lng
+              .chars()
+              .into_iter()
+              .take(2)
+              .for_each(|x| end += x.len_utf8());
+            let root_lng = &full_lng[..end];
+            // Filter to check if is in `Config.langs`
+            let list: Vec<String> = langs
+              .iter()
+              .filter(|lng| lng.as_str() == root_lng)
+              .cloned()
+              .collect();
+            if let Some(lng) = list.get(0) {
+              // Set if it is found and available
+              lang = lng.to_owned();
+            }
+          }
+        }
+        Err(e) => println!("Error: {}", e),
+      }
+    }
+    // println!("Lang: {}",&lang);
+    lang
+  }
+  /// Get `Schema` Config
+  pub async fn is_admin(&self, ky: String) -> bool {
+    self.env().appkey == ky
+  }
+ 
+  /// Render a template with context and load `data_path` from `root_path` in case is not empty
+  /// it use a `data_hash` for context with data if it is not empty
+  /// if errors it will render a `fallback_template`
+  #[allow(clippy::too_many_arguments,clippy::missing_errors_doc)]
+  pub async fn render_template(
+    &self,
+    ctx: &mut tera::Context,
+    root_path: &str,
+    template_name: &str,
+    fallback_template: &str,
+    data_path: &str,
+    data_hash: &mut HashMap<String, String>,
+    app_module: &str,
+  ) -> Result<String,AppError> {
+    let tera: &mut Tera = &mut self.tera(); //.to_owned();
+    if ! app_module.is_empty() {
+      AppData::load_modules(&self.env(), &app_module,ctx);
+    }
+    Ok(if data_path.is_empty() {
+      tera
+        .render(&template_name, &ctx)
+        .map_err(|e| AppError::ErrorInternalServerError(format!("Error: {}", e)))?
+    } else {
+      let render_fallback = |e| -> Result<String,AppError> {
+         println!("Error parsing data {}:\n{}", &data_path, e);
+         Ok(tera
+          .render(fallback_template, &tera::Context::new())
+          .map_err(|e| AppError::ErrorInternalServerError(format!("Template error {}",e)))?)
+      };
+      match hash_from_data(format!("{}/{}", &root_path, &data_path).as_str(), ctx, data_hash, true) {
+        Ok(_) => {
+          let mut need_reload = false;
+					if let Some(has_reload) = ctx.get("need_reload") {
+						if has_reload  == "true"  {
+							need_reload=true;
+						}
+					}
+          for (key, value) in data_hash.iter() {
+            if key.as_str() == "need_reload" {
+              need_reload = true; 
+            } else {
+              ctx.insert(key, value);
+            }
+          }
+          if need_reload {
+            match read_path_file(&root_path, &template_name, "content") {
+              Ok(tpl) => {
+                println!("reload {}",&template_name);
+                tera.add_raw_templates(vec![(&template_name, tpl)])
+                .map_err(|e| AppError::ErrorInternalServerError(format!("Error reload: {}", e)))?
+              },
+              Err(e) => return render_fallback(e),
+            };
+          }
+          tera
+            .render(&template_name, &ctx)
+            .map_err(|e| AppError::ErrorInternalServerError(format!("Error render: {}", e)))?
+        }
+        Err(e) => {
+          println!("Error parsing data {}:\n{}", &data_path, e);
+          return render_fallback(e);
+        }
+      }
+    })
+  }
+  #[allow(clippy::too_many_arguments,clippy::missing_errors_doc)]
+  pub async fn render_page(
+    &self,
+    ctx: &mut tera::Context,
+    root_path: &str,
+    template_name: &str,
+    fallback_template: &str,
+    data_path: &str,
+    data_hash: &mut HashMap<String, String>,
+    app_module: &str,
+  ) -> Result<String,AppError> {
+    let tera = &self.tera();
+    let res = match self
+      .render_template(
+        ctx,
+        root_path,
+        template_name,
+        fallback_template,
+        data_path,
+        data_hash,
+        app_module,
+      )
+      .await
+    {
+      Ok(res) => res,
+      Err(e) => {
+        println!(
+          "Error render template + data {} + {}:\n{}",
+          &template_name, &data_path, e
+        );
+        tera
+          .render(&fallback_template, &tera::Context::new())
+          .map_err(|e| AppError::ErrorInternalServerError(format!("Template error {}",e)))?
+      }
+    };
+//    Ok(HttpResponse::Ok().content_type("text/html").body(res))
+    Ok(res)
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub async fn render_to(
+    &self,
+    template_name: &str,
+    data_path: &str,
+    data_hash: &mut HashMap<String, String>,
+    app_module: &str,
+  ) -> Result<String,AppError> {
+    let app_env = &self.env();
+    // From Default Context
+    let mut ctx = self.ctx().to_owned();
+    let fallback_template = "index.html";
+    self
+      .render_page(
+        &mut ctx,
+        app_env.get_curr_websrvr_config().templates_path.as_str(),
+        template_name,
+        fallback_template,
+        data_path,
+        data_hash,
+        app_module,
+      )
+      .await
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub fn token_from_header(&self) -> anyhow::Result<String> {
+    if let Some(header) = self.header.get("authorization") {
+      let auth_header = match from_utf8(header.as_bytes()) {
+        Ok(v) => v,
+        Err(_) => return Err(AuthError::NoAuthHeaderFoundError.into()),
+      };
+      if !auth_header.starts_with(BEARER_PREFIX) {
+        return Err(AuthError::InvalidAuthHeaderFormatError.into());
+      }
+      let without_prefix = auth_header.trim_start_matches(BEARER_PREFIX);
+      Ok(without_prefix.to_owned())
+    } else {
+      Err(AuthError::NoAuthHeaderFoundError.into())
+    }
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub async fn token_session(&self, login: &LoginRequest) -> anyhow::Result<String> {
+    // dbg!("{}",login);
+    if let Some(user) = &self.auth_store.users
+      .read()
+      .await
+      .iter()
+      .find(|(_, v)| v.name == *login.name)
+       // .filter(|(_, v)| *v.name == name)
+       //  .nth(0)
+    {
+      let shdw = &self.auth_store.shadows.read().await;
+       if let Some(usr_shdw) = shdw.get(&String::from(user.0)) {
+          // println!("{}",&usr_shdw.passwd);
+          // println!("{}",&login.passwd);
+          // println!("{}",&user.0);
+          if usr_shdw.passwd == login.passwd {
+            // println!("OK ");
+            let token = uuid::Uuid::new_v4().to_string();
+            self.auth_store.sessions.write().await.insert(token.clone(), String::from(user.0));
+            return Ok(token);
+          }
+       }
+       Err(AuthError::UserNotFoundError.into())
+      //shdw.get(usr_key);
+    } else {
+    Err(AuthError::UserNotFoundError.into())
+    }
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub async fn user_authentication(&self) -> anyhow::Result<UserCtx> {
+    let token = self.token_from_header().unwrap_or_else(|e| { 
+			if envmnt::get_isize("DEBUG", 0) > 0 {
+				println!("{}",e); 
+			}
+			String::from("")
+		});
+		println!("token: {}",token); 
+    self.check_authentication(token).await
+  }
+	#[allow(clippy::missing_errors_doc)]
+  pub async fn user_role(&self) -> String {
+    let token = self.token_from_header().unwrap_or_else(|e| {
+			if envmnt::get_isize("DEBUG", 0) > 0 {
+				println!("{}",e); 
+			}
+			String::from("")
+		});
+		if token.is_empty() {
+			return String::from("");
+		}
+		let role: String;
+		if let Some(user_id) = self.auth_store.sessions.read().await.get(&token) {
+      if let Some(user) = self.auth_store.users.read().await.get(user_id) {
+				role = format!("{}",user.role);
+			} else {
+				role = String::from("");
+			}
+		} else {
+			role = String::from("");
+		}
+		role
+  }
+	#[allow(clippy::missing_errors_doc)]
+  pub async fn get_user(&self) -> User {
+    let token = self.token_from_header().unwrap_or_else(|e| {
+			if envmnt::get_isize("DEBUG", 0) > 0 {
+				println!("{}",e); 
+			}
+			String::from("")
+		});
+		if !token.is_empty() {
+			if let Some(user_id) = self.auth_store.sessions.read().await.get(&token) {
+				if let Some(user) = self.auth_store.users.read().await.get(user_id) {
+					return user.to_owned() 
+				}
+			}
+		}
+		User::default()
+  }
+  #[allow(clippy::missing_errors_doc)]
+  pub async fn check_authentication(&self, token: String) -> anyhow::Result<UserCtx> {
+    if let Some(user_id) = self.auth_store.sessions.read().await.get(&token) {
+      if let Some(user) = self.auth_store.users.read().await.get(user_id) {
+        // println!("{} - {} - {}",&user.role.as_str(), &self.path.as_str(), &self.method.as_str());
+        match self.auth_store.enforcer.enforce(
+          (&user.role.as_str(), &self.path.as_str(), &self.method.as_str())
+        )
+        {
+          Ok(authorized) => {
+            if authorized {
+              Ok(UserCtx { user_id: user.user_id.to_owned(), token })
+            } else {
+              Err(AuthError::UnauthorizedError.into())
+            }
+          }
+          Err(e) => {
+            eprintln!("error during authorization: {}", e);
+            Err(AuthError::AuthorizationError.into())
+          }
+        }
+      } else {
+        Err(AuthError::InvalidTokenError.into())
+      }
+    } else {
+      Err(AuthError::InvalidTokenError.into())
+    }
+  }
+}