How to use Cookie Session JWT in koa?

How to use Cookie Session JWT in koa? Application and realization principle of Cookie, Session, JWT in koa.

Cookies

  • The HTTP protocol is stateless, but in WEB applications, it is necessary to share sessions between multiple requests, so cookies appear
  • Cookie is the data stored on the client for the purpose of identifying the user’s identity and for session tracking

The server sets cookies: when the client accesses the server for the first time, it will send cookies to the client through the response header, and the attributes are separated by semicolon spaces

The client receives and saves the cookie: when the client requests the server again, it will carry the cookie to the server, and the cookie itself is a request header

How to use Cookie Session JWT in koa?
Image by Gerd Altmann from Pixabay 

Important attributes

Simulate two different domain names by modifying the local hosts file.

# hosts
127.0.0.1   znlive.com
127.0.0.1   jhrs.com

How to use Cookie Session JWT in koa?

Attributesinstruction
name=valueKey-value pairs, you can Set to save Key/Value
DomainFor a domain name to take effect, it can cross the parent domain and the subdomain,The default is the current domain name
expires/max-ageCookie survival time, expires absolute time, max-age relative time unit second
secureWhen the secure value is true, the cookie is invalid in HTTP and only takes effect under https
PathIndicates that the path affected by the cookie default is/can be accessed. like When the path does not match, the browser will not send this cookie
httpOnlyIt means that the browser cannot be obtained through code to prevent XSS attacks, but it can be changed by manually modifying the console.
How to use Cookie Session JWT in koa?

How to use Cookie Session JWT in koa

npm install koa koa-router

How to use Cookie Session JWT in koa?

const Koa = require('koa');
const Router = require('koa-router');
const querystring = require('querystring') // used to parse and format the URL query string

const app = new Koa();
const router = new Router();

// koa operation cookie implementation principle
app.use(async (ctx, next) => {
  // Extend a method of setting cookies
  let cookieArr = [];
  ctx.req.getCookie = function (key) {
    let cookies = ctx.req.headers['cookie']; // name=xx; age=yy => name=xx&age=yy
    let cookieObj = querystring.parse(cookies,';')
    return cookieObj[key] ||''
  }
  ctx.res.setCookie = function (key, value, options={}) {
    let args = []; // Each cookie attribute collection
    options.domain && args.push(`domain=${options.domain}`);
    options.maxAge && args.push(`max-age=${options.maxAge}`);
    options.httpOnly && args.push(`httpOnly=${options.httpOnly}`);
    options.path && args.push(`path=${options.path}`);

    cookieArr.push(`${key}=${value}; ${args.join(';')}`);
    ctx.res.setHeader('Set-Cookie', cookieArr); // string array
  }
  await next();
})
 
router.get('/read', async (ctx, next) => {

   // Encapsulate by yourself
   ctx.body = ctx.req.getCookie('name') ||'empty';

  // koa implementation
  // ctx.body = ctx.cookies.get('name') ||'empty';

  // Native usage
  // ctx.body = ctx.req.headers['cookie'] ||'empty'; // request header
})
router.get('/write', async (ctx, next) => {
  
  // Encapsulate by yourself
  ctx.res.setCookie('name','nn', {domain:'.znlive.com'}); // Restrict accessible domain names
  ctx.res.setCookie('age', '12', {httpOnly: true,path:'/write'}); // Restrict accessible paths

  // koa implementation
  // ctx.cookies.set('name','nn', {domain:'.znlive.com'});
  // ctx.cookies.set('age', '12', {httpOnly: true,path:'/write'});

  // Native usage
  // ctx.res.setHeader('Set-Cookie','name=yy');
  // ctx.res.setHeader('Set-Cookie','age=15'); // Set a cookie, set cookie again will overwrite the previous one
  // ctx.res.setHeader('Set-Cookie', ['name=yy; domain=.znlive.com','age=15; path=/; max-age=10; httpOnly=true']); // When setting multiple cookies, an array of strings can be accepted

  ctx.body ='write ok';
})

app.use(router.routes())
app.listen(4000);

How to use Cookie Session JWT in koa?

Principle of cookie signature

Cookies are usually generated by the server and exist on the client side. With each request sent to the server side, the front-end stored data can be manually tampered with by the user.

Therefore, it is possible to sign the cookie to make it relatively safe, generate an identification based on the content of the cookie, retain the original content, and add a configuration {signed: true} for each request to verify the signature

const Koa = require('koa');
const Router = require('koa-router');
const querystring = require('querystring');
const crypto = require('crypto');

const app = new Koa();
const router = new Router();

app.keys = ['ya'];
// base64Url needs special treatment + = /
const sign = value => crypto.createHmac('sha1',app.keys.join('')).update(value).digest('base64').replace(/\+/g,'-'). replace(/\=/g,'').replace(/\//g,'_');
app.use(async (ctx, next) => {
  let cookieArr = [];
  ctx.req.getCookie = function (key, options = {}) {
    let cookies = ctx.req.headers['cookie'];
    let cookieObj = querystring.parse(cookies,';')
     
    if(options.signed){
      // The passed signature is consistent with the result obtained by the latest calculation, indicating that it has not been modified
      if(cookieObj[key +'.sig'] === sign(`${key}=${cookieObj[key]}`)){
        return cookieObj[key];
      }else {
        return''
      }
    }
    return cookieObj[key] ||''
  }
  ctx.res.setCookie = function (key, value, options = {}) {
    let args = [];
    let keyValue = `${key}=${value}`
    options.domain && args.push(`domain=${options.domain}`);
    options.maxAge && args.push(`max-age=${options.maxAge}`);
    options.httpOnly && args.push(`httpOnly=${options.httpOnly}`);
    options.path && args.push(`path=${options.path}`);
    options.signed && cookieArr.push(`${key}.sig=${sign(keyValue)}`); // Whether to enable cookie signature

    cookieArr.push(`${keyValue}; ${args.join(';')}`);
    ctx.res.setHeader('Set-Cookie', cookieArr); // string array
  }
  await next();
})

// app.keys required for signed cookies
router.get('/visit', async (ctx, next) => {
  // Koa implementation
  // let count = ctx.cookies.get('visit',{ signed: true }) || 0;
  // let visitCount = Number(count) + 1;
  // ctx.cookies.set('visit', visitCount, {signed: true });
  // ctx.body = `you visit ${visitCount}`

  // Encapsulate by yourself
  let count = ctx.req.getCookie('visit', {signed: true }) || 0;
  let visitCount = Number(count) + 1;
  ctx.res.setCookie('visit', visitCount, {signed: true });
  ctx.body = `ya visit: ${visitCount}`

})

app.use(router.routes())
app.listen(3000);

How to use Cookie Session JWT in koa?

How to use Cookie Session JWT in koa?
How to use Cookie Session JWT in koa?

Precautions

May be tampered with by the client verify the legality of the signature before use,
Don’t store sensitive data, Such as user password, account balance
Every time request will automatically carry cookies, try to reduce the size of cookies
Set the correct domain and pathTo reduce data transmission

Session

Is another mechanism to record customer status, the difference is in the Cookie saved in the client browser, and the session saved in the server upper
Store the user’s corresponding information on the server, the server can store sensitive information, and the session itself is cookie-based and safer than cookies

At the same time, session has no persistence function and needs to be used with database or redis

Realization principle

npm install uuid

How to use Cookie Session JWT in koa?

const Koa = require('koa');
const Router = require('koa-router');
const uuid = require('uuid');

const app = new Koa();
const router = new Router();

app.keys = ['ya']

const session = {}; // Used to store the mapping relationship between users and information, invisible to the browser
const cardName ='connect_sig';
router.get('/cut', async (ctx, next) => {
   let id = ctx.cookies.get(cardName, {signed:true});
   if(id && session[id]){
     session[id].mny -= 20;
     ctx.body = `mny:` + session[id].mny;
   }else{
     let cardId = uuid.v4();
     session[cardId] = {mny: 500 };
     ctx.cookies.set(cardName, cardId,{httpOnly:true,signed:true}); // Only one identifier is stored in the cookie, and there is no real data
     ctx.body = `mny 500`;
   }
})
app.use(router.routes())
app.listen(3000);

How to use Cookie Session JWT in koa?

JWT

JSON Web Token(JWT)It is currently the most popular cross-domain authentication solution. JWT is not encrypted by default and anyone can read it, so don’t put important information in this section.
Solve the problem: session does not support distributed architecture, cannot support horizontal expansion, only through the database to save the session data to achieve sharing. If the persistence layer fails, authentication will fail.

Advantages: The server does not save any session data, that is, the server becomes stateless, making it easier to expand.

How to use Cookie Session JWT in koa?
How to use Cookie Session JWT in koa?

How to use

  1. In the Authorization field of the HTTP request header information Authorization: Bearer <token>
  2. If it is a post request, it can also be placed in the request body, depending on which authentication method the backend uses
  3. It is transmitted via url https://znlive.com/pwa?token=xxxxx, but it is generally not recommended to use it, because there will be connection sharing and security risks

composition

JWT contains .split three parts used , How to use Cookie Session JWT in koa?

1. HeaderHeader.Payload.Signature

{ "alg": "HS256", "typ": "JWT"}   
// algorithm => HMAC SHA256
// type => JWT

How to use Cookie Session JWT in koa?

2. Payload content JWT stipulates 7 official fields

iss (issuer): Issuer
exp (expiration time): expiration time
sub (subject): Subject
aud (audience): audience
nbf (Not Before): effective time
iat (Issued At): Issuing time
jti (JWT ID): number

How to use Cookie Session JWT in koa?

3. Signature

Sign the first two parts to prevent data tampering HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

How to use Cookie Session JWT in koa?

Practical application

npm install koa-bodyparser jwt-simple

const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const jwt = require('jwt-simple');
const app = new Koa();
const router = new Router();
app.use(bodyParser())

// log in
router.post('/login', async (ctx, next) => {
  let {username, password} = ctx.request.body;
  if (username =='admin' && password =='admin') {
    // let token = jwt.encode(username,'znlive.com'); // jwt-simple implementation
    let token = myJwt.encode(username,'znlive.com'); // implement it yourself
    ctx.body = {
      code: 200,
      data: {
        token,
        username
      }
    }
  }
})
// Verify if there is permission
router.get('/validate', async (ctx, next) => {
  let authorization = ctx.get('authorization');
  if(authorization){
    let [,token] = authorization.split('');
    try{
      // let r = jwt.decode(token,'znlive.com'); // jwt-simple implementation
      let r = myJwt.decode(token,'znlive.com'); // implement it yourself
      ctx.body = {
        code:200,
        data:{
          username:r
        }
      }
    }catch{
      ctx.body = {
        code:401,
        data:'token has expired'
      }
    }
  }
})

app.use(router.routes())
app.listen(3000);

How to use Cookie Session JWT in koa?

Realization principle

// The token component consists of three segments, 1, the fixed format represents the type 2, the content 3 is the signature
//'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ImFkbWluIg.xJ0xCP2SXSaJSC-Q1PXuByHdJlBUHCNjdGRU4XW0abU'
const myJwt = {
  sign(str,secret){
    str = require('crypto').createHmac('sha256',secret).update(str).digest('base64');
    return this.toBase64Escape(str);
  },
  toBase64(content){ // object to base64 needs to be converted to buffer => base64
    let source = typeof content ==='string'? content: JSON.stringify(content);
    return this.toBase64Escape(Buffer.from(source).toString('base64'));
  },
  toBase64Escape(base64){
    return base64.replace(/\+/g,'-').replace(/\//g,'_').replace(/\=/g,'');
  },
  encode(username, secret){ // Converting to base64 is not for security, but for transmission on the network
    let header = this.toBase64({typ:'JWT',alg:'HS256'});
    let content = this.toBase64(username);
    let sign = this.sign([header, content].join('.'),secret)
    return header +'.' + content +'.' + sign
  },
  base64urlUnescape(str){
    str += new Array(5-str.length% 4).join('=');
    return str.replace(/\-/g,'+').replace(/_/g,'/');
  },
  // The same content generates the same signature, you can add some expiration time and other information
  // A signature is generated from the content, and vice versa, the signature is verified. To know if the content has changed
  decode(token,secret){
    let [header, content, sign] = token.split('.');
    let newSign = this.sign([header, content].join('.'),secret);
    if(sign === newSign){ // The data in the content line2 must be reliable at this time
      return Buffer.from(this.base64urlUnescape(content),'base64').toString();
    }else{
      throw new Error('token has expired')
    }
  }
}

How to use Cookie Session JWT in koa?

  • The cookie feature can be automatically carried every time a request is made, and the user login function can be realized. Use cookies to identify users,
  • If you simply use cookies, it is not recommended to store sensitive information if it is hijacked. (Cookies exist on the client side and are not secure, users can tamper with them)
  • Each browser generally has a limit on the size of the request header. The cookie cannot be larger than 4k. If the cookie is too large, the page will be blank. Every time you visit the server, traffic will be wasted (setting cookies reasonably);
  • Session: Store the user’s corresponding information on the server, the server can store sensitive information, and the session itself is cookie-based and safer than cookies;
  • localStorage: The data still exists when the browser is turned off, unless it is manually cleared, there is a size limit of about 5M, and the request will not be carried;
  • sessionStorage: the page will not be destroyed if it is not closed (purpose: storage of scroll bar address when single page application is accessed)
  • token -> jwt -> jsonwebtoken does not require server storage, no cross-domain restrictions, it is not recommended to store sensitive information

Leave a Comment