Alexander Garcia
Parsing Cookie strings and transforming them into usable JavaScript because backend engineers were busy
Read time is about 6 minutes
Alexander Garcia is an effective JavaScript Engineer who crafts stunning web experiences.
Alexander Garcia is a meticulous Web Architect who creates scalable, maintainable web solutions.
Alexander Garcia is a passionate Software Consultant who develops extendable, fault-tolerant code.
Alexander Garcia is a detail-oriented Web Developer who builds user-friendly websites.
Alexander Garcia is a passionate Lead Software Engineer who builds user-friendly experiences.
Alexander Garcia is a trailblazing UI Engineer who develops pixel-perfect code and design.
Hello all, this will be a slightly in-depth post that I hope helps some developers out there.
We are using OAuth and one of the ways we are using session management on the Front-end is by providing cookies of each of the Access Token and Refresh Token expiration dates.
I assume that the reader is knowledgable on using .split, .map, and .reduce array methods.
Our Backend is built using Ruby on Rails and the problem I was facing when the response cookies were set were:
// document.cookie "FLIPPER_ID=flipper_on; token_info=%7B%3Aaccess_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A11%3A58.265440745+UTC+%2B00%3A00%2C+%3Arefresh_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A36%3A58.147084176+UTC+%2B00%3A00%7D";
From here I had a few requirements of my own to ensure the session management work as I expected
const oAuthCookieObject = document.cookie // Creates an array of each cookie .split(";") // Maps the cookies to <key>=<value> pairs .map((cookie) => cookie.split("=")) /* Reduces it down to a single object of our access token and refresh tokens by checking if our cookieKey includes the 'info_token' value we are looking for */ .reduce( (_, [cookieKey, cookieValue]) => ({ ...(cookieKey.includes("info_token") && { ...formatOurCookie(decodeURIComponent(cookieValue)), }), }), {} );
document.cookie// original string "FLIPPER_ID=flipper_on; info_token="[ // after .split ("FLIPPER_ID=flipper_on", "info_token=%7B%3Aaccess_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A11%3A58.265440745+UTC+%2B00%3A00%2C+%3Arefresh_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A36%3A58.147084176+UTC+%2B00%3A00%7D") ];
// original array after .split [ 'FLIPPER_ID=flipper_on', 'info_token=%7B%3Aaccess_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A11%3A58.265440745+UTC+%2B00%3A00%2C+%3Arefresh_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A36%3A58.147084176+UTC+%2B00%3A00%7D' ] // after we use .map [ ['FLIPPER_ID', 'flipper_on'] ['info_token', ['%7B%3Aaccess_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A11%3A58.265440745+UTC+%2B00%3A00%2C+%3Arefresh_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A36%3A58.147084176+UTC+%2B00%3A00%7D'] ]
// String before decodeURIComponent is called const nonDecoded = "%7B%3Aaccess_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A11%3A58.265440745+UTC+%2B00%3A00%2C+%3Arefresh_token_expiration%3D%3EFri%2C+17+Jun+2022+16%3A36%3A58.147084176+UTC+%2B00%3A00%7D"; // String after decodedURIComponent is called const decoded = "{:access_token_expiration=>Fri,+17+Jun+2022+16:11:58.265440745+UTC++00:00,+:refresh_token_expiration=>Fri,+17+Jun+2022+16:36:58.147084176+UTC++00:00}";
formatOurCookie functionfunction formatOurCookie(unformattedCookieString) { return ( unformattedCookieString // Creates an array by splitting on ',+:' to get the access token and refresh token .split(",+:") .reduce((obj, cookieVal) => { // Destructure the key|value pair of the token's name and its expiration date and uses Regex to remove {: and } const [key, val] = cookieVal.replace(/{:|}/g, "").split("=>"); // Update the value by replacing the '+' with spaces and removing the UTC timezone ending const formattedValue = val .replaceAll("++00:00", "") .replaceAll("+", " "); // Return's the accumulator and the key|value pair with a usable JavaScript Date object return { ...obj, [key]: new Date(formattedValue), }; }, {}) ); }
formatOurCookie functionunformattedCookieString parameter which will be a decodeURIComponent string and use the split method on ',+:' to get the access_token_expiration and the refresh_token_expiration into an array// original string "{:access_token_expiration=>Fri,+17+Jun+2022+16:11:58.265440745+UTC++00:00,+:refresh_token_expiration=>Fri,+17+Jun+2022+16:36:58.147084176+UTC++00:00}"[ // array split on the `',+:'` ("{:access_token_expiration=>Fri,+17+Jun+2022+16:11:58.265440745+UTC++00:00", "refresh_token_expiration=>Fri,+17+Jun+2022+16:36:58.147084176+UTC++00:00}") ];
Use the .reduce method to loop through the split array with the goal being to reduce it into a single object.
We want to destructure the key|value pairs by
a. First removing all instances of :{ and } from the string.
// original (removes `:{`) "{:access_token_expiration=>Fri,+17+Jun+2022+16:11:58.265440745+UTC++00:00"; // after removes `:{` "access_token_expiration=>Fri,+17+Jun+2022+16:11:58.265440745+UTC++00:00"; // after removes `}` "refresh_token_expiration=>Fri,+17+Jun+2022+16:36:58.147084176+UTC++00:00";
b. Then by splitting the string on the => using the .split method
// original "access_token_expiration=>Fri,+17+Jun+2022+16:11:58.265440745+UTC++00:00"[ // transformed ("access_token_expiration", "Fri,+17+Jun+2022+16:11:58.265440745+UTC++00:00") ];
c. Format the key's value into a usable format by replacing the + with a single space and removing the ++00:00
// original "Fri,+17+Jun+2022+16:11:58.265440745+UTC++00:00"; // formatted "Fri, 17 Jun 2022 16:11:58.265440745 UTC";
const oAuthCookieObject = document.cookie .split(";") .map((cookie) => cookie.split("=")) .reduce( (_, [cookieKey, cookieValue]) => ({ ...(cookieKey.includes("info_token") && { ...formatOAuthCookie(decodeURIComponent(cookieValue)), }), }), {} ); function formatOurCookie(unformattedCookieString) { return unformattedCookieString.split(",+:").reduce((obj, cookieVal) => { const [key, val] = cookieVal.replace(/{:|}/g, "").split("=>"); const formattedValue = val.replaceAll("++00:00", "").replaceAll("+", " "); return { ...obj, [key]: new Date(formattedValue), }; }, {}); }
Hopefully some of you found that useful. Cheers! 🎉