captcha-igid
Version:
Authorization service for an intergalactic session
330 lines (292 loc) • 12.1 kB
JavaScript
const { GeneralAuth, SessionManager } = require('copious-transitions')
//
const g_crypto = require('crypto-wraps')
let cookie_count = 0
async function derivation_keys(sessM) {
let axiom_pair = await g_crypto.axiom_keypair_promise()
sessM.private_derivation = axiom_pair.privateKey
sessM.public_derivation = axiom_pair.publicKey
sessM.axiom_pub_key_str = await g_crypto.ecdh_to_str(sessM.public_derivation,"jwk");
}
class CaptchaSessionManager extends SessionManager {
constructor(exp_app,db_obj,business,trans_engine,token_storage) { //
super(exp_app,db_obj,business,trans_engine,token_storage) //
// ---- ---- ---- ---- ---- ---- ---- ---- ----
derivation_keys(this)
}
app_set_user_cookie(res,session_token) { // express token here
if ( res ) {
res.cookie(this.user_cookie,session_token, { maxAge: this.max_age_user_cookie, httpOnly: true });
}
}
//
app_user_release_cookie(res) {
if ( res ) {
res.clearCookie(this.user_cookie); // delete the cookie
}
}
//
set_cookie(res,cookie_id,value,age) {
if ( res ) {
res.cookie(cookie_id,value, { maxAge: age, httpOnly: true });
}
}
//
release_cookie(res,cookie_id) {
if ( res ) {
res.clearCookie(cookie_id);
}
}
foreign_auth(post_body) { // check that it is in supported strategies
return( false )
}
post_body_decode(udata) { // don't decode strings ... will not be sent uri endcoded
return udata
}
// consult ORACLE and add this to something like a tangle... ... use g_crypto... for now
async loginTransitionFields(transtion_object,post_body,user) {
transtion_object.token = 'user+' + g_crypto.gen_nonce() // this token identifies this transition object (for secondar)
let nonce = '' + g_crypto.gen_nonce()
// // consult an ORACLE (hold onto it until after handshake)
let sess_tok = await this.do_hash(post_body[this.hashables.field1] + post_body[this.hashables.field2] + nonce) // this is the session identifier just getting started.
transtion_object.session_token = sess_tok
transtion_object.strategy = post_body.strategy
transtion_object.add_extra_field("host_public_derivation")
transtion_object.add_extra_field("ctext")
transtion_object.add_extra_field("iv_nonce")
//
transtion_object.elements = { "match" : transtion_object.token } // token for passing login (not the session)
//
this.stash_session_token(user,transtion_object,sess_tok) // user has _tracking
}
//
addSession(key,session_token) {
// tell the ORACLE that this is OK
// after the ORACLE and service have talked....
super.addSession(key,session_token)
//
// publish this session to maps outside this machine (if the db customization does not)
// ref (see) this.db.set_session_key_value(session_token,key)
//
}
// //
async login_transition(user,transtion_object,post_body) {
if ( user.ucwid === post_body.ucwid ) { // user is from the database cache for users
//
transtion_object.secondary_action = true
let key_key = G_users_trns.kv_store_key()
transtion_object._t_u_key = key_key
transtion_object.user_key = user[key_key] // database key (not a crypto key)
await this.loginTransitionFields(transtion_object,post_body,user)
//
let pub_derive_key = user.public_derivation
// user sends public derivation key (or is from UCWID) ... user retains private side elliptical
//
let local_priv_ky = this.private_derivation // private part of derivation key
//
transtion_object.host_public_derivation = this.axiom_pub_key_str // the key that will wrap the challenge
//
let challenge = g_crypto.gen_nonce()
let iv_nonce = g_crypto.gen_nonce()
// // ---- encipher the challenge
let import_pub_key = false
try {
import_pub_key = await g_crypto.importECDHKey(pub_derive_key,"jwk")
} catch (e){
console.log(e)
}
if ( import_pub_key ) {
transtion_object.ctext = await g_crypto.derived_encipher_message(challenge,import_pub_key,local_priv_ky,iv_nonce)
transtion_object.iv_nonce = iv_nonce // send the nonce
// save clear for handshake ... the user should have sent a signer public key (match)
transtion_object.elements["clear"] = challenge // save the challenge for later
transtion_object.elements["verify_key"] = user.signer_public_key
}
//
}
}
// //
/**
* registration_transition
*
* (Override)
*
* @param {object} post_body
* @param {object} transtion_object
* @returns
*/
async registration_transition(post_body,transtion_object) {
try {
if ( post_body.public_derivation ) {
let all = true
await this.db.store_user(post_body,all) // only store in the LRU and keep a derivation key handy
}
} catch (e) {
return false
}
return true
}
// //
async fetch_user_public_key(user) {
try {
let all = true
let user_data = await this.db.fetch_user(user,all) // only store in the LRU and keep a derivation key handy
if ( user_data ) {
return user_data.public_derivation
}
} catch (e) {
console.log(e)
}
return false
}
// //
async process_user(user_op,body,res) {
this.set_cookie(res,'copious+tester',`yozie-dozie${cookie_count++}`,60000)
let pkey = G_users_trns.primary_key()
//
let transtionObj = await super.process_user(user_op,body,res,pkey) // generic process use
//
if ( user_op === 'register' ) {
transtionObj.check_public = false
}
if ( (user_op === 'register') && (transtionObj.amelioritive_action !== undefined) ) {
delete transtionObj.amelioritive_action
transtionObj.check_public = true
}
// at this point the transition object has two tokens...
if ( G_users_trns.action_selector(user_op) ) {
transtionObj[pkey] = body[pkey]
} else {
if ( user_op === 'logout' ) {
// req.logout() // an express app method...
}
}
return(transtionObj)
}
//
// process_asset(asset_id,post_body) {}
async feasible(transition,post_body,req) { // is the transition something that can be done?
if ( G_captcha_trns.tagged(transition) || G_contact_trns.tagged(transition) ) {
return(true)
} else if ( G_admin_actions_trns.tagged(transition) ) {
return await this.trans_engine.special_user_status(post_body)
}
return(super.feasible(transition,post_body,req))
}
//
async process_transition(transition,post_body,req) {
//
let trans_object = super.process_transition(transition,post_body,req)
//
if ( G_captcha_trns.tagged(transition) ) {
post_body._uuid_prefix = G_captcha_trns.uuid_prefix()
} else if ( G_contact_trns.tagged(transition) ) {
trans_object.secondary_action = false
} else if ( G_admin_actions_trns.tagged(transition) ) {
trans_object.secondary_action = false
trans_object.action = post_body.action
trans_object.file = post_body.file
}
//
return(trans_object)
}
//
async match(post_body,transtion_object) {
if ( G_captcha_trns.tagged(transtion_object.tobj.asset_key) ) {
post_body._t_match_field = post_body[G_captcha_trns.match_key()]
} else if ( G_users_trns.action_selector(transtion_object.action) ) {
post_body._t_match_field = post_body[G_users_trns.match_key()]
} else if ( G_users_trns.secondary_action_selector(transtion_object.action) ) {
let signature = post_body.signature // the user has signed the clear challenge
let clear_data = transtion_object.elements["clear"]
let verify_key = transtion_object.elements["verify_key"] // verify key from primary action...
let OK = await g_crypto.verifier(clear_data,signature,verify_key) // use the pulic verification key from the user identity
return OK
} else {
return false
}
return super.match(post_body,transtion_object)
}
//
async finalize_transition(transition,post_body,elements,req) {
if ( G_captcha_trns.tagged(transition) ) {
if ( post_body._t_match_field ) {
super.update_session_state(transition,post_body,req)
let finalization_state = {
"state" : "captcha-in-flight",
"OK" : "true"
}
// set a cookie for use by other micro services
return(finalization_state)
}
} else if ( G_contact_trns.tagged(transition) ) {
let state = "nothing"
if ( post_body.remove === "true" ) {
this.db.remove(transition,post_body)
} else {
this.db.store(transition,post_body)
state = "stored"
}
let finalization_state = {
"state" : state,
"OK" : "true"
}
return(finalization_state)
} else if ( G_admin_actions_trns.tagged(transition) ) {
this.trans_engine.admin_transition_finalizer(post_body)
let finalization_state = {
"state" : state,
"OK" : "true"
}
return(finalization_state)
}
let finalization_state = {
"state" : "ERROR",
"OK" : "false"
}
return(finalization_state)
}
//
which_uploaded_files(req,post_body) {
if ( req ) {
let files = req.files
return(files) // the application should handle this
}
return([])
}
//
update_session_state(transition,session_token,req) { // req for session cookies if any
if ( G_users_trns.action_selector(body.action) ) {
this.addSession(req.body.cid,session_token)
res.cookie(this.user_cookie, session_token, { maxAge: 900000, httpOnly: true });
} else {
return super.update_session_state(transition,session_token,req)
}
return true
}
//
sess_data_accessor() {
return G_users_trns.sess_data_accessor()
}
//
key_for_user() { // communicate to the general case which key to use
let key_key = G_users_trns.kv_store_key()
return(key_key)
}
//
async initialize_session_state(transition,session_token,transtionObj,res) {
if ( G_users_trns.tagged('user') ) { // application gives the correct field to the general initialize_session_state
transtionObj._t_u_key = G_users_trns.session_key()
transtionObj._db_session_key = transtionObj[transtionObj._t_u_key] // e.g. ["ucwid"]
return await super.initialize_session_state(transition,session_token,transtionObj,res)
}
return undefined
}
}
class CaptchaAuth extends GeneralAuth {
constructor() {
super(CaptchaSessionManager) // intializes general authorization with the customized session manager class.
}
}
var session_producer = new CaptchaAuth()
module.exports = session_producer;