Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Using JSON Web Tokens & Varnish to cache content for logged-in users - DrupalCon Vienna 2017

Ca901ddcea38854b9783781c91fc87c9?s=47 Thijs Feryn
September 26, 2017

Using JSON Web Tokens & Varnish to cache content for logged-in users - DrupalCon Vienna 2017

Ca901ddcea38854b9783781c91fc87c9?s=128

Thijs Feryn

September 26, 2017
Tweet

Transcript

  1. By Thijs Feryn Using JSON Web Tokens & Varnish to

    cache content for logged-in users
  2. Slow websites suck

  3. Web performance is an essential part of the user experience

  4. SLOW Slowdown ~ downtime

  5. None
  6. Heavy load

  7. Mo money Mo servers

  8. Cache

  9. Don’t recompute if the data hasn’t changed

  10. Normally User Server

  11. With Varnish User Varnish Server

  12. None
  13. Hi, I’m Thijs

  14. I’m @ThijsFeryn on Twitter

  15. I’m an Evangelist At

  16. I’m an Evangelist At

  17. None
  18. Let's dive right in!

  19. Website for big TV channel

  20. Video content is very popular

  21. Drupal 7 with Varnish

  22. Hit rate is great!

  23. Business requirement

  24. Force users to log in to watch full episodes

  25. None
  26. SESS8776ed48f0b08839e2cd7485bc08b4d0= O12RKyXsM8_f8vC3xJPE4WtGyGH4eirXhT_AcN5 MTGI

  27. Cache bypass

  28. User-specific content

  29. Compromise: disable login when it's too busy

  30. What if we could create cache variations for logged-in users?

  31. MariaDB [drupal]> select * from main_sessions; +-----+---------------------------------------------+------+-----------------+------------+-------+---------+ | uid |

    sid | ssid | hostname | timestamp | cache | session | +-----+---------------------------------------------+------+-----------------+------------+-------+---------+ | 2 | 26lbuJAm37NrudWYaJ8TcW1u7J7WFs7TjAWOhjv-krI | | 141.135.242.208 | 1505304003 | 0 | | | 2 | O12RKyXsM8_f8vC3xJPE4WtGyGH4eirXhT_AcN5MTGI | | 141.135.242.208 | 1505307466 | 0 | | +-----+---------------------------------------------+------+-----------------+------------+-------+---------+ SESS8776ed48f0b08839e2cd7485bc08b4d0= O12RKyXsM8_f8vC3xJPE4WtGyGH4eirXhT_AcN5MTGI Client-side Server-side
  32. How do you identify a logged-in user without accessing the

    backend every time?
  33. Push session information from the server to the client

  34. JSON Web Tokens

  35. Marco Pivetta introduced me to JWT https://twitter.com/ocramius

  36. JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOi JhZG1pbiIsImV4cCI6MTQ5NTUyODc1NiwibG9naW4iOnRyd WV9.u4Idy-SYnrFdnH1h9_sNc4OasORBJcrh2fPo1EOTre8 ✓3 parts ✓Dot separated ✓Base64 encoded

    JSON ✓Header ✓Payload ✓Signature (HMAC with secret)
  37. eyJzdWIiOiJhZG1pbiIsIm V4cCI6MTQ5NTUyODc1Niwi bG9naW4iOnRydWV9 { "alg": "HS256", "typ": "JWT" } {

    "sub": "admin", "exp": 1495528756, "login": true } HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret ) eyJhbGciOiJIUzI1NiIsI nR5cCI6IkpXVCJ9 u4Idy- SYnrFdnH1h9_sNc4OasOR BJcrh2fPo1EOTre8
  38. https://jwt.io

  39. None
  40. JWT Cookie:jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiJhZG1pbiIsImV4cCI6MTQ5NTUyODc1NiwibG9naW4iOn RydWV9.u4Idy-SYnrFdnH1h9_sNc4OasORBJcrh2fPo1EOTre8 ✓Stored in a cookie ✓Can be

    validated by Varnish ✓Payload can be processed by any language (e.g. Javascript)
  41. Drupal creates JWT alongside the regular session

  42. Drupal template loads stateful data from JWT in Javascript

  43. Andreas De Rijcke built the custom Drupal module to handle

    JWT https://twitter.com/andreasderijcke https://gitlab.com/andreasderijcke/jwt-cookie
  44. { "iss": "drupaljwt.feryn.eu", "jti": "mIEBDk2zFP3nqrlDuEScTsc4fVocTmhrkp36fQwJQdA", "iat": 1505313422, "exp": 1507313422, "uid":

    "2", "roles": "authenticated user", "data": "{\"html\":{\"user-name\":\"test1\"},\"attr\":{\"user- url\":{\"href\":\"\\/user\\/2\"}}}" } Issuer
  45. { "iss": "drupaljwt.feryn.eu", "jti": "mIEBDk2zFP3nqrlDuEScTsc4fVocTmhrkp36fQwJQdA", "iat": 1505313422, "exp": 1507313422, "uid":

    "2", "roles": "authenticated user", "data": "{\"html\":{\"user-name\":\"test1\"},\"attr\":{\"user- url\":{\"href\":\"\\/user\\/2\"}}}" } Drupal session ID
  46. { "iss": "drupaljwt.feryn.eu", "jti": "mIEBDk2zFP3nqrlDuEScTsc4fVocTmhrkp36fQwJQdA", "iat": 1505313422, "exp": 1507313422, "uid":

    "2", "roles": "authenticated user", "data": "{\"html\":{\"user-name\":\"test1\"},\"attr\":{\"user- url\":{\"href\":\"\\/user\\/2\"}}}" } Issued at
  47. { "iss": "drupaljwt.feryn.eu", "jti": "mIEBDk2zFP3nqrlDuEScTsc4fVocTmhrkp36fQwJQdA", "iat": 1505313422, "exp": 1507313422, "uid":

    "2", "roles": "authenticated user", "data": "{\"html\":{\"user-name\":\"test1\"},\"attr\":{\"user- url\":{\"href\":\"\\/user\\/2\"}}}" } Expires at
  48. { "iss": "drupaljwt.feryn.eu", "jti": "mIEBDk2zFP3nqrlDuEScTsc4fVocTmhrkp36fQwJQdA", "iat": 1505313422, "exp": 1507313422, "uid":

    "2", "roles": "authenticated user", "data": "{\"html\":{\"user-name\":\"test1\"},\"attr\":{\"user- url\":{\"href\":\"\\/user\\/2\"}}}" } User ID
  49. { "iss": "drupaljwt.feryn.eu", "jti": "mIEBDk2zFP3nqrlDuEScTsc4fVocTmhrkp36fQwJQdA", "iat": 1505313422, "exp": 1507313422, "uid":

    "2", "roles": "authenticated user", "data": "{\"html\":{\"user-name\":\"test1\"},\"attr\":{\"user- url\":{\"href\":\"\\/user\\/2\"}}}" } User roles
  50. { "iss": "drupaljwt.feryn.eu", "jti": "mIEBDk2zFP3nqrlDuEScTsc4fVocTmhrkp36fQwJQdA", "iat": 1505313422, "exp": 1507313422, "uid":

    "2", "roles": "authenticated user", "data": "{\"html\":{\"user-name\":\"test1\"},\"attr\":{\"user- url\":{\"href\":\"\\/user\\/2\"}}}" } HTML content to be renderd by javascript
  51. Meanwhile I wrote some VCL code

  52. Using vmod_digest for HMAC & base64

  53. vcl 4.0;
 
 import std;
 import var;
 import cookie;
 import

    digest;
 
 acl internal {
 "192.168.20.0"/24;
 }
 
 backend default {
 .host = "127.0.0.1";
 .port = "8080";
 }
  54. sub vcl_recv {
 var.set(“key",std.fileread("/home/drupal/jwt.key"));
 
 if ((req.method != "GET" &&

    req.method != "HEAD") || req.http.Authorization) {
 return (pass);
 }
 
 if (req.url ~ "^/(cron|install|update)\.php$" && !client.ip ~ internal) {
 return (synth(404, "Page not found."));
 }
 
 if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm|woff)(\?[wd=.-]+)?$") {
 unset req.http.Cookie;
 return(hash);
 }
 
 if (req.http.Cookie) {
 cookie.parse(req.http.cookie);
 
 if(req.http.cookie ~ ".*(SESS[a-z0-9]+)=.*") {
 var.set("sessionCookie",regsub(req.http.cookie,".*(SESS[a-z0-9]+)=.*","\1"));
 }
 
 cookie.filter_except("PHPSESSID,NO_CACHE,ci_session,CI_SESSION,authtoken,jwt_cookie,"+var.get("sessionCookie"));
 
 set req.http.cookie = cookie.get_string();
 
 if (req.http.Cookie ~ "^\s*$") {
 unset req.http.Cookie;
 }
 }
 call jwt; if(var.get("roles") ~ "administrator") {
 std.log("Administrators bypass the cache");
 return(pass);
 }
 
 if(req.url == "/login" && req.http.X-login =="true") {
 return(synth(302,"/user"));
 }

  55. 
 
 if(var.get("roles") ~ "administrator") {
 std.log("Administrators bypass the cache");


    return(pass);
 }
 
 if(req.url == "/login" && req.http.X-login == "true") {
 return(synth(302,"/user"));
 }
 
 if(req.url == "/user" && req.http.X-login != "true") {
 return (hash);
 }
 
 if (req.url == "/user/password" || req.url == "/user/register") {
 return(hash);
 }
 
 if(req.url ~ "^/user/login") {
 return(hash);
 }
 
 if (req.url == "/status.php" ||
 req.url == "/ooyala/ping" ||
 req.url == "/user" ||
 req.url ~ "^/admin/.*$" ||
 req.url ~ "^/info/.*$" ||
 req.url ~ "^/flag/.*$" ||
 req.url ~ "^.*/ajax/.*$" ||
 req.url ~ "^.*/ahah/.*$" ||
 req.url ~ "^/rest/.*$" ||
 req.url ~ "^/users?/.*$"
 ) {
 return (pass);
 }
 
 return(hash);
 }
  56. sub vcl_hash {
 if (req.http.X-Forwarded-Proto) {
 hash_data(req.http.X-Forwarded-Proto);
 }
 }
 


    sub vcl_backend_response {
 set beresp.http.x-url = bereq.url;
 set beresp.http.x-host = bereq.http.host;
 
 if (bereq.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[wd=.-]+)?$") {
 unset beresp.http.set-cookie;
 }
 }
 sub vcl_synth {
 if (resp.status == 301 || resp.status == 302) {
 set resp.http.location = resp.reason;
 set resp.reason = "Moved";
 return (deliver);
 }
 }
  57. sub jwt {
 if(cookie.isset("jwt_cookie")) {
 var.set("token", cookie.get("jwt_cookie"));
 var.set("header", regsub(var.get("token"),"([^\.]+)\.[^\.]+\.[^\.]+","\1"));
 var.set("type",

    regsub(digest.base64url_decode(var.get("header")),{"^.*?"typ"\s*:\s*"(\w+)".*?$"},"\1"));
 var.set("algorithm", regsub(digest.base64url_decode(var.get("header")),{"^.*?"alg"\s*:\s*"(\w+)".*? $"},"\1"));
 
 if(var.get("type") == "JWT" && var.get("algorithm") == "HS256") {
 var.set("rawPayload",regsub(var.get("token"),"[^\.]+\.([^\.]+)\.[^\.]+$","\1"));
 var.set("signature",regsub(var.get("token"),"^[^\.]+\.[^\.]+\.([^\.]+)$","\1"));
 var.set("currentSignature",digest.base64url_nopad_hex(digest.hmac_sha256(var.get("key"),var.get("header") + "." + var.get("rawPayload"))));
 var.set("payload", digest.base64url_decode(var.get("rawPayload")));
 var.set("exp",regsub(var.get("payload"),{"^.*?"exp"\s*:\s*([0-9]+).*?$"},"\1"));
 var.set("jti",regsub(var.get("payload"),{"^.*?"jti"\s*:\s*"([a-z0-9A-Z_\-]+)".*?$"},"\1"));
 var.set("userId",regsub(var.get("payload"),{"^.*?"uid"\s*:\s*"([0-9]+)".*?$"},"\1"));
 var.set("roles",regsub(var.get("payload"),{"^.*?"roles"\s*:\s*"([a-z0-9A-Z_\-, ]+)".*?$"},"\1"));
 
 if(std.time(var.get("exp"),now) >= now && cookie.get(var.get("sessionCookie")) == var.get(“jti”) && var.get("signature") == var.get("currentSignature")) {
 set req.http.X-login="true";
 }
 } 
 }
 
 if(req.url ~ "/node/2" && req.url !~ "^/user/login") {
 if(req.http.X-login != "true") {
 return(synth(302,"/user/login?destination=" + req.url));
 }
 }
 }
  58. sub jwt {
 if(cookie.isset("jwt_cookie")) {
 var.set("token", cookie.get("jwt_cookie"));
 var.set("header", regsub(var.get("token"),"([^\.]+)\.[^\.]+\.[^\.]+","\1"));
 var.set("type",

    regsub(digest.base64url_decode(var.get("header")),{"^.*?"typ"\s*:\s*"(\w+)".*?$"},"\1"));
 var.set("algorithm", regsub(digest.base64url_decode(var.get("header")),{"^.*?"alg"\s*:\s*"(\w+)".*? $"},"\1"));
 
 if(var.get("type") == "JWT" && var.get("algorithm") == "HS256") {
 var.set("rawPayload",regsub(var.get("token"),"[^\.]+\.([^\.]+)\.[^\.]+$","\1"));
 var.set("signature",regsub(var.get("token"),"^[^\.]+\.[^\.]+\.([^\.]+)$","\1"));
 var.set("currentSignature",digest.base64url_nopad_hex(digest.hmac_sha256(var.get("key"),var.get("header") + "." + var.get("rawPayload"))));
 var.set("payload", digest.base64url_decode(var.get("rawPayload")));
 var.set("exp",regsub(var.get("payload"),{"^.*?"exp"\s*:\s*([0-9]+).*?$"},"\1"));
 var.set("jti",regsub(var.get("payload"),{"^.*?"jti"\s*:\s*"([a-z0-9A-Z_\-]+)".*?$"},"\1"));
 var.set("userId",regsub(var.get("payload"),{"^.*?"uid"\s*:\s*"([0-9]+)".*?$"},"\1"));
 var.set("roles",regsub(var.get("payload"),{"^.*?"roles"\s*:\s*"([a-z0-9A-Z_\-, ]+)".*?$"},"\1"));
 
 if(std.time(var.get("exp"),now) >= now && cookie.get(var.get("sessionCookie")) == var.get(“jti”) && var.get("signature") == var.get("currentSignature")) {
 set req.http.X-login="true";
 }
 } 
 }
 
 if(req.url ~ "/node/2" && req.url !~ "^/user/login") {
 if(req.http.X-login != "true") {
 return(synth(302,"/user/login?destination=" + req.url));
 }
 }
 }
  59. sub jwt {
 if(cookie.isset("jwt_cookie")) {
 var.set("token", cookie.get("jwt_cookie"));
 var.set("header", regsub(var.get("token"),"([^\.]+)\.[^\.]+\.[^\.]+","\1"));
 var.set("type",

    regsub(digest.base64url_decode(var.get("header")),{"^.*?"typ"\s*:\s*"(\w+)".*?$"},"\1"));
 var.set("algorithm", regsub(digest.base64url_decode(var.get("header")),{"^.*?"alg"\s*:\s*"(\w+)".*? $"},"\1"));
 
 if(var.get("type") == "JWT" && var.get("algorithm") == "HS256") {
 var.set("rawPayload",regsub(var.get("token"),"[^\.]+\.([^\.]+)\.[^\.]+$","\1"));
 var.set("signature",regsub(var.get("token"),"^[^\.]+\.[^\.]+\.([^\.]+)$","\1"));
 var.set("currentSignature",digest.base64url_nopad_hex(digest.hmac_sha256(var.get("key"),var.get("header") + "." + var.get("rawPayload"))));
 var.set("payload", digest.base64url_decode(var.get("rawPayload")));
 var.set("exp",regsub(var.get("payload"),{"^.*?"exp"\s*:\s*([0-9]+).*?$"},"\1"));
 var.set("jti",regsub(var.get("payload"),{"^.*?"jti"\s*:\s*"([a-z0-9A-Z_\-]+)".*?$"},"\1"));
 var.set("userId",regsub(var.get("payload"),{"^.*?"uid"\s*:\s*"([0-9]+)".*?$"},"\1"));
 var.set("roles",regsub(var.get("payload"),{"^.*?"roles"\s*:\s*"([a-z0-9A-Z_\-, ]+)".*?$"},"\1"));
 
 if(std.time(var.get("exp"),now) >= now && cookie.get(var.get("sessionCookie")) == var.get(“jti”) && var.get("signature") == var.get("currentSignature")) {
 set req.http.X-login="true";
 }
 } 
 } 
 if(req.url ~ "/node/2" && req.url !~ "^/user/login") {
 if(req.http.X-login != "true") {
 return(synth(302,"/user/login?destination=" + req.url));
 }
 }
 }
  60. sub jwt {
 if(cookie.isset("jwt_cookie")) {
 var.set("token", cookie.get("jwt_cookie"));
 var.set("header", regsub(var.get("token"),"([^\.]+)\.[^\.]+\.[^\.]+","\1"));
 var.set("type",

    regsub(digest.base64url_decode(var.get("header")),{"^.*?"typ"\s*:\s*"(\w+)".*?$"},"\1"));
 var.set("algorithm", regsub(digest.base64url_decode(var.get("header")),{"^.*?"alg"\s*:\s*"(\w+)".*? $"},"\1"));
 
 if(var.get("type") == "JWT" && var.get("algorithm") == "HS256") {
 var.set("rawPayload",regsub(var.get("token"),"[^\.]+\.([^\.]+)\.[^\.]+$","\1"));
 var.set("signature",regsub(var.get("token"),"^[^\.]+\.[^\.]+\.([^\.]+)$","\1"));
 var.set("currentSignature",digest.base64url_nopad_hex(digest.hmac_sha256(var.get("key"),var.get("header") + "." + var.get("rawPayload"))));
 var.set("payload", digest.base64url_decode(var.get("rawPayload")));
 var.set("exp",regsub(var.get("payload"),{"^.*?"exp"\s*:\s*([0-9]+).*?$"},"\1"));
 var.set("jti",regsub(var.get("payload"),{"^.*?"jti"\s*:\s*"([a-z0-9A-Z_\-]+)".*?$"},"\1"));
 var.set("userId",regsub(var.get("payload"),{"^.*?"uid"\s*:\s*"([0-9]+)".*?$"},"\1"));
 var.set("roles",regsub(var.get("payload"),{"^.*?"roles"\s*:\s*"([a-z0-9A-Z_\-, ]+)".*?$"},"\1"));
 
 if(std.time(var.get("exp"),now) >= now && cookie.get(var.get("sessionCookie")) == var.get(“jti”) && var.get("signature") == var.get("currentSignature")) {
 set req.http.X-login="true";
 }
 } 
 }
 
 if(req.url ~ "/node/2" && req.url !~ "^/user/login") {
 if(req.http.X-login != "true") {
 return(synth(302,"/user/login?destination=" + req.url));
 }
 }
 }
  61. sub jwt {
 if(cookie.isset("jwt_cookie")) {
 var.set("token", cookie.get("jwt_cookie"));
 var.set("header", regsub(var.get("token"),"([^\.]+)\.[^\.]+\.[^\.]+","\1"));
 var.set("type",

    regsub(digest.base64url_decode(var.get("header")),{"^.*?"typ"\s*:\s*"(\w+)".*?$"},"\1"));
 var.set("algorithm", regsub(digest.base64url_decode(var.get("header")),{"^.*?"alg"\s*:\s*"(\w+)".*? $"},"\1"));
 
 if(var.get("type") == "JWT" && var.get("algorithm") == "HS256") {
 var.set("rawPayload",regsub(var.get("token"),"[^\.]+\.([^\.]+)\.[^\.]+$","\1"));
 var.set("signature",regsub(var.get("token"),"^[^\.]+\.[^\.]+\.([^\.]+)$","\1"));
 var.set("currentSignature",digest.base64url_nopad_hex(digest.hmac_sha256(var.get("key"),var.get("header") + "." + var.get("rawPayload"))));
 var.set("payload", digest.base64url_decode(var.get("rawPayload")));
 var.set("exp",regsub(var.get("payload"),{"^.*?"exp"\s*:\s*([0-9]+).*?$"},"\1"));
 var.set("jti",regsub(var.get("payload"),{"^.*?"jti"\s*:\s*"([a-z0-9A-Z_\-]+)".*?$"},"\1"));
 var.set("userId",regsub(var.get("payload"),{"^.*?"uid"\s*:\s*"([0-9]+)".*?$"},"\1"));
 var.set("roles",regsub(var.get("payload"),{"^.*?"roles"\s*:\s*"([a-z0-9A-Z_\-, ]+)".*?$"},"\1"));
 
 if(std.time(var.get("exp"),now) >= now && cookie.get(var.get("sessionCookie")) == var.get(“jti”) && var.get("signature") == var.get("currentSignature")) {
 set req.http.X-login="true";
 }
 } 
 }
 
 if(req.url ~ "/node/2" && req.url !~ "^/user/login") {
 if(req.http.X-login != "true") {
 return(synth(302,"/user/login?destination=" + req.url));
 }
 }
 }
  62. sub jwt {
 if(cookie.isset("jwt_cookie")) {
 var.set("token", cookie.get("jwt_cookie"));
 var.set("header", regsub(var.get("token"),"([^\.]+)\.[^\.]+\.[^\.]+","\1"));
 var.set("type",

    regsub(digest.base64url_decode(var.get("header")),{"^.*?"typ"\s*:\s*"(\w+)".*?$"},"\1"));
 var.set("algorithm", regsub(digest.base64url_decode(var.get("header")),{"^.*?"alg"\s*:\s*"(\w+)".*? $"},"\1"));
 
 if(var.get("type") == "JWT" && var.get("algorithm") == "HS256") {
 var.set("rawPayload",regsub(var.get("token"),"[^\.]+\.([^\.]+)\.[^\.]+$","\1"));
 var.set("signature",regsub(var.get("token"),"^[^\.]+\.[^\.]+\.([^\.]+)$","\1"));
 var.set("currentSignature",digest.base64url_nopad_hex(digest.hmac_sha256(var.get("key"),var.get("header") + "." + var.get("rawPayload"))));
 var.set("payload", digest.base64url_decode(var.get("rawPayload")));
 var.set("exp",regsub(var.get("payload"),{"^.*?"exp"\s*:\s*([0-9]+).*?$"},"\1"));
 var.set("jti",regsub(var.get("payload"),{"^.*?"jti"\s*:\s*"([a-z0-9A-Z_\-]+)".*?$"},"\1"));
 var.set("userId",regsub(var.get("payload"),{"^.*?"uid"\s*:\s*"([0-9]+)".*?$"},"\1"));
 var.set("roles",regsub(var.get("payload"),{"^.*?"roles"\s*:\s*"([a-z0-9A-Z_\-, ]+)”.*?$"},"\1")); 
 if(std.time(var.get("exp"),now) >= now && cookie.get(var.get("sessionCookie")) == var.get(“jti”) && var.get("signature") == var.get("currentSignature")) {
 set req.http.X-login="true";
 }
 } 
 }
 
 if(req.url ~ "/node/2" && req.url !~ "^/user/login") {
 if(req.http.X-login != "true") {
 return(synth(302,"/user/login?destination=" + req.url));
 }
 }
 }
  63. Cache variations

  64. Vary: X-login Vary header sent by Drupal X-login header set

    by Varnish Creates cache variations in Varnish
  65. Let's talk Drupal

  66. Modules ✓Varnish ✓Key (store JWT key) ✓HTTP Response Headers +

    UI (set cache-control) ✓JWT cookie (set JWT token) ✓JWT cookie Example (replace content & display meta info)
  67. None
  68. None
  69. None
  70. function _jwt_cookie_cookie_create($user, $data, $cached = FALSE) { $cookie_domain = _jwt_cookie_get_domain();

    $builder = new Builder(); $signer = new Sha256(); // Build the token. $expiration = time() + ini_get('session.cookie_lifetime'); $builder ->setIssuer($cookie_domain) ->setId(session_id(), TRUE) ->setIssuedAt(time()) ->setExpiration($expiration) ->set('uid', $user->uid) ->set('roles', implode(',', $user->roles)); // When we use data from cache, we don't need to clean the data again. if (!$cached) { // Get and clean the data. Discard the rest. $attr = isset($data['attr']) ? $data['attr'] : array(); $html = isset($data['html']) ? $data['html'] : array(); unset($data); foreach ($html as $tag => $value) { $html[$tag] = _jwt_cookie_clean_value($value); } $data['html'] = $html; foreach ($attr as $tag => $attributes) { foreach ($attributes as $attribute => $value) { $attributes[$attribute] = _jwt_cookie_clean_value($value); } } $data['attr'] = $attr; // Store in cache. _jwt_cookie_cache_set($user->uid, $data); } Uses "lcobucci/jwt" Composer package
  71. ->set('uid', $user->uid) ->set('roles', implode(',', $user->roles)); // When we use data

    from cache, we don't need to clean the data again. if (!$cached) { // Get and clean the data. Discard the rest. $attr = isset($data['attr']) ? $data['attr'] : array(); $html = isset($data['html']) ? $data['html'] : array(); unset($data); foreach ($html as $tag => $value) { $html[$tag] = _jwt_cookie_clean_value($value); } $data['html'] = $html; foreach ($attr as $tag => $attributes) { foreach ($attributes as $attribute => $value) { $attributes[$attribute] = _jwt_cookie_clean_value($value); } } $data['attr'] = $attr; // Store in cache. _jwt_cookie_cache_set($user->uid, $data); } $data = json_encode($data); $builder->set('data', $data); // Wrap up and set the cookie. $signature = _jwt_cookie_get_signature_key(); $builder->sign($signer, $signature); $token = $builder->getToken(); return setcookie(JWT_COOKIE_NAME, $token, $expiration, '/', '.' . $cookie_domain); }
  72. https://gitlab.com/andreasderijcke/jwt-cookie

  73. Push session information from the server to the client

  74. None
  75. https://feryn.eu https://twitter.com/ThijsFeryn https://instagram.com/ThijsFeryn

  76. None