{"componentChunkName":"component---src-templates-blog-list-template-js","path":"/148","result":{"data":{"allMarkdownRemark":{"edges":[{"node":{"excerpt":"For over 20 years, BroadcastMed has been innovating digital strategies for healthcare providers and organizations. They work with the best…","fields":{"slug":"/identity/broadcastmed-loginradius-customer-success-story/"},"html":"<p>For over 20 years, <a href=\"https://www.broadcastmed.com/\">BroadcastMed</a> has been innovating digital strategies for healthcare providers and organizations. They work with the best brands in healthcare to help them develop and deliver relevant video-based learning content for physicians and other healthcare professionals.</p>\n<p>Committed to producing quality healthcare content in the clinical setting, they were the first in the world to live stream surgical procedures on the internet to provide an intimate look inside the operating room.</p>\n<h2 id=\"the-situation\" style=\"position:relative;\"><a href=\"#the-situation\" aria-label=\"the situation permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>The Situation</h2>\n<p>Being in the forefront of innovation in their industry, BroadcastMed was looking for a way to improve their registration system for a better user experience and partner connectivity while ensuring data security.</p>\n<h2 id=\"the-challenges\" style=\"position:relative;\"><a href=\"#the-challenges\" aria-label=\"the challenges permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>The Challenges</h2>\n<p>BroadcastMed had a few concerns that they wanted to address as they evaluated the LoginRadius solution:</p>\n<ul>\n<li><strong>The security of their user data</strong></li>\n<li>\n<ul>\n<li>As BroadcastMed works with numerous healthcare brands and retrieves sensitive identity information from the physicians and healthcare professionals who accesses the digital content, data security is of utmost importance when evaluating a <a href=\"https://www.loginradius.com/blog/identity/customer-identity-and-access-management/\">customer identity and access  management solution</a>.</li>\n</ul>\n</li>\n<li><strong>How to provide users across systems with the ability to register for programs</strong></li>\n<li>\n<ul>\n<li>BroadcastMed has numerous web properties hosting a multitude of video content for different medical fields. As such, they need to ensure that they have a centralized registration point and that their users are able to effortlessly navigate throughout the different content without interruption.</li>\n</ul>\n</li>\n<li><strong>Needed to be able to integrate single sign on with their partners</strong></li>\n<li>\n<ul>\n<li>BroadcastMed needed to make sure that the solution they chose has the capability to integrate Single Sign On with their partners as the network grows and expands.</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"the-loginradius-solution\" style=\"position:relative;\"><a href=\"#the-loginradius-solution\" aria-label=\"the loginradius solution permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>The LoginRadius Solution</h2>\n<ul>\n<li><strong>Integrating Single Sign-On with BroadcastMed's Partner</strong></li>\n<li>\n<ul>\n<li>\n<p>With LoginRadius' assistance, BroadcastMed was able to implement <a href=\"https://www.loginradius.com/single-sign-on-overview/\">Single Sign-On</a> with an important syndication partner to provide learning content to the partner's network of physicians and healthcare workers.</p>\n<p>This allowed those users to directly access BroadcastMed's content from the partner's portal, as well as making sure that the individual user's learning progress is safely stored.</p>\n</li>\n</ul>\n</li>\n<li><strong>Doximity as a Custom ID Provider</strong></li>\n<li>\n<ul>\n<li>Doximity is a social network specific to medical professionals and therefore is not a readily available social login provider. LoginRadius, however, was able to integrate Doximity as a custom identity provider for industry-specific social login.</li>\n</ul>\n</li>\n<li><strong>National Provider Identification (NPI) integration</strong></li>\n<li>\n<ul>\n<li>LoginRadius provided a custom registration NPI registration page where the users' NPI numbers are verified with the NPI registry in real-time. As well, the form autofills with the information that are retrieved from the NPI registry.</li>\n</ul>\n</li>\n<li><strong>Custom Object</strong></li>\n<li>\n<ul>\n<li>LoginRadius configured the <a href=\"https://www.loginradius.com/custom-object/\">custom object</a> to ensure that all of the data required by BroadcastMed are properly and securely stored, including the data retrieved via the NPI registry and the learning progress (or viewing history) of individual users, to name a few.</li>\n</ul>\n</li>\n<li><strong>Marketo integration</strong></li>\n<li>\n<ul>\n<li>A custom <a href=\"https://www.loginradius.com/integrations/marketo/\">Marketo integration</a> was configured for BroadcastMed to sync the desired user data from LoginRadius to Marketo in real time. This helped BroadcastMed to strategically develop solutions that help their clients more deeply engage with their audiences.</li>\n</ul>\n</li>\n</ul>\n<iframe width=\"744\" height=\"418\" src=\"https://www.youtube.com/embed/gUKIrBO_Ltg\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n<p>BroadcastMed - LoginRadius Customer Success Story</p>\n<p><a href=\"https://www.loginradius.com/contact-us?utm_source=blog&#x26;utm_medium=web&#x26;utm_campaign=broadcastmed-loginradius-customer-success-story\"><img src=\"/1bebf239d110701b9b534d7eb481a5ac/BD-Plexicon1-1024x310-1.webp\" alt=\"book-a-demo-loginradius\"></a></p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n</style>","frontmatter":{"date":"July 05, 2018","updated_date":null,"description":"BroadcastMed has been innovating digital strategies for healthcare providers and organizations. How loginradius assist them in managing user identities.","title":"BroadcastMed, a LoginRadius Customer Success Story","tags":["media-and-publication"],"pinned":null,"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.7857142857142858,"src":"/static/15e3271f09c6fce827bea18b5c1cf6a0/58556/LR-and-BroadcastMed.webp","srcSet":"/static/15e3271f09c6fce827bea18b5c1cf6a0/61e93/LR-and-BroadcastMed.webp 200w,\n/static/15e3271f09c6fce827bea18b5c1cf6a0/1f5c5/LR-and-BroadcastMed.webp 400w,\n/static/15e3271f09c6fce827bea18b5c1cf6a0/58556/LR-and-BroadcastMed.webp 800w,\n/static/15e3271f09c6fce827bea18b5c1cf6a0/45c4b/LR-and-BroadcastMed.webp 958w","sizes":"(max-width: 800px) 100vw, 800px"}}},"author":{"id":"Karl Wittig","github":null,"avatar":null}}}},{"node":{"excerpt":"Nowadays, many API providers support JSONP requests. One reason for this is that most web browsers disable cross-domain requests when using…","fields":{"slug":"/engineering/understanding-jsonp/"},"html":"<p>Nowadays, many API providers support JSONP requests. One reason for this is that most web browsers disable cross-domain requests when using basic Ajax.</p>\n<p>For example, if your website has the domain \"a.com\", it will use JavaScript hosted on a.com. When the a.com JavaScript makes an Ajax call to make a request on b.com, most web browsers would automatically deem the Ajax call as insecure and disable it. This is called the Same-Origin Policy and web browsers have this to prevent malicious scripts from sending off information to a different domain. Because you need the a.com JavaScript to access b.com to provide your service, this seems to pose a pretty big issue … JSONP to the rescue!</p>\n<p>Before understanding JSONP, we already know JSON is an object notation of JavaScript. The \"P\" stands for padding. So it’s a padded JSON, and, to be more specific, the JSON object is padded with a JavaScript function! It looks like this:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk11\">jsFunction</span><span class=\"mtk1\">({</span><span class=\"mtk8\">&quot;name&quot;</span><span class=\"mtk12\"> :</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;Ash Ketchum&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk8\">&quot;role&quot;</span><span class=\"mtk12\"> :</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;Pokemon trainer&quot;</span><span class=\"mtk1\">});</span></span></code></pre>\n<p>Thus, technically, any call that retrieves JSONP, sends off an executable JavaScript line, if and only if your page has a JavaScript function that has the same function name that’s returned in the JSONP!</p>\n<p>Let’s look at an example: say the user is on a.com and the browser is using JavaScript hosted on a.com. Then I shouldn't have any issues making Ajax calls to a.com. Ajax GET requests to b.com however, would fail. To avoid this, I first create a method in the JavaScript code that's located on a.com with the following signature</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"1\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">function</span><span class=\"mtk1\"> </span><span class=\"mtk11\">getData</span><span class=\"mtk1\">(</span><span class=\"mtk12\">data</span><span class=\"mtk1\">){</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk3\">// use this data</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>With this, an Ajax call using JSONP will pass through fine and return this data:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"2\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk11\">getData</span><span class=\"mtk1\">({</span><span class=\"mtk8\">&quot;name&quot;</span><span class=\"mtk12\"> :</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;test&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk8\">&quot;value&quot;</span><span class=\"mtk12\"> :</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;test Value&quot;</span><span class=\"mtk1\">});</span></span></code></pre>\n<p>After processing the request, the web browser will call the \"getData\" function because whenever a JavaScript tag is loaded, it gets executed.</p>\n<p>Now, the JSON object will get passed as an argument to the getData function as the data parameter. So, you can think of the getData as a callback method of the request.</p>\n<p>You can see the below code clearly</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"3\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">function</span><span class=\"mtk1\"> </span><span class=\"mtk11\">addJavascriptFile</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">function</span><span class=\"mtk1\"> (</span><span class=\"mtk12\">url</span><span class=\"mtk1\">, </span><span class=\"mtk12\">context</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">context</span><span class=\"mtk1\"> = </span><span class=\"mtk12\">context</span><span class=\"mtk1\"> || </span><span class=\"mtk12\">document</span><span class=\"mtk1\">;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">head</span><span class=\"mtk1\"> = </span><span class=\"mtk12\">context</span><span class=\"mtk1\">.</span><span class=\"mtk11\">getElementsByTagName</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;head&#39;</span><span class=\"mtk1\">)\\[</span><span class=\"mtk7\">0</span><span class=\"mtk1\">\\];</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">js</span><span class=\"mtk1\"> = </span><span class=\"mtk12\">context</span><span class=\"mtk1\">.</span><span class=\"mtk11\">createElement</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;script&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">js</span><span class=\"mtk1\">.</span><span class=\"mtk12\">src</span><span class=\"mtk1\"> = </span><span class=\"mtk12\">url</span><span class=\"mtk1\">;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">js</span><span class=\"mtk1\">.</span><span class=\"mtk12\">type</span><span class=\"mtk1\"> = </span><span class=\"mtk8\">&quot;text/JavaScript&quot;</span><span class=\"mtk1\">;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">head</span><span class=\"mtk1\">.</span><span class=\"mtk11\">appendChild</span><span class=\"mtk1\">(</span><span class=\"mtk12\">js</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk15\">return</span><span class=\"mtk1\"> </span><span class=\"mtk12\">js</span><span class=\"mtk1\">;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">function</span><span class=\"mtk1\"> </span><span class=\"mtk11\">getJsonp</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">function</span><span class=\"mtk1\"> (</span><span class=\"mtk12\">url</span><span class=\"mtk1\">, </span><span class=\"mtk12\">handle</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk3\">//creating random name of function as to not conflict with others</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">func</span><span class=\"mtk1\"> = </span><span class=\"mtk8\">&#39;jsonpCallback&#39;</span><span class=\"mtk1\"> + </span><span class=\"mtk10\">Math</span><span class=\"mtk1\">.</span><span class=\"mtk11\">floor</span><span class=\"mtk1\">((</span><span class=\"mtk10\">Math</span><span class=\"mtk1\">.</span><span class=\"mtk11\">random</span><span class=\"mtk1\">() \\* </span><span class=\"mtk7\">1000000000000000000</span><span class=\"mtk1\">) + </span><span class=\"mtk7\">1</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk3\">//adding randomly created function to global window object</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">window</span><span class=\"mtk1\">\\[</span><span class=\"mtk12\">func</span><span class=\"mtk1\">\\] = </span><span class=\"mtk4\">function</span><span class=\"mtk1\"> (</span><span class=\"mtk12\">data</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk3\">//calling handle</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk11\">handle</span><span class=\"mtk1\">(</span><span class=\"mtk12\">data</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk3\">//removing random named declared function</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">window</span><span class=\"mtk1\">\\[</span><span class=\"mtk12\">func</span><span class=\"mtk1\">\\] = </span><span class=\"mtk4\">function</span><span class=\"mtk1\"> () {};</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk3\">//removing added js</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">document</span><span class=\"mtk1\">.</span><span class=\"mtk12\">head</span><span class=\"mtk1\">.</span><span class=\"mtk11\">removeChild</span><span class=\"mtk1\">(</span><span class=\"mtk12\">js</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk3\">//manipulating and adding js file to head</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">endurl</span><span class=\"mtk1\"> = </span><span class=\"mtk12\">url</span><span class=\"mtk1\">.</span><span class=\"mtk11\">indexOf</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;?&#39;</span><span class=\"mtk1\">) != -</span><span class=\"mtk7\">1</span><span class=\"mtk1\"> ? </span><span class=\"mtk12\">url</span><span class=\"mtk1\"> + </span><span class=\"mtk8\">&#39;&amp;amp;callback=&#39;</span><span class=\"mtk1\"> + </span><span class=\"mtk12\">func</span><span class=\"mtk1\"> : </span><span class=\"mtk12\">url</span><span class=\"mtk1\"> + </span><span class=\"mtk8\">&#39;?callback=&#39;</span><span class=\"mtk1\"> + </span><span class=\"mtk12\">func</span><span class=\"mtk1\">;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">var</span><span class=\"mtk1\"> </span><span class=\"mtk12\">js</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">addJavascriptFile</span><span class=\"mtk1\">(</span><span class=\"mtk12\">endurl</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>The above code is not doing any magic, it will accept the URL of the API and add a parameter callback with a random name, and also create a global method with this same random name. It will then create a script tag and add the complete URL to src of this tag.</p>\n<p>The API will read the callback parameter from the query string and, if the callback parameter has the value \"jsonCallback\", the response will be as follows:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"javascript\" data-index=\"4\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk11\">jsonpCallback</span><span class=\"mtk1\">({</span><span class=\"mtk8\">&quot;name&quot;</span><span class=\"mtk12\"> :</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;test&quot;</span><span class=\"mtk1\">, </span><span class=\"mtk8\">&quot;value&quot;</span><span class=\"mtk12\"> :</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;test Value&quot;</span><span class=\"mtk1\">});</span></span></code></pre>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .dark-default-dark .mtk11 { color: #DCDCAA; }\n  .dark-default-dark .mtk1 { color: #D4D4D4; }\n  .dark-default-dark .mtk8 { color: #CE9178; }\n  .dark-default-dark .mtk12 { color: #9CDCFE; }\n  .dark-default-dark .mtk4 { color: #569CD6; }\n  .dark-default-dark .mtk3 { color: #6A9955; }\n  .dark-default-dark .mtk7 { color: #B5CEA8; }\n  .dark-default-dark .mtk15 { color: #C586C0; }\n  .dark-default-dark .mtk10 { color: #4EC9B0; }\n</style>","frontmatter":{"date":"June 29, 2018","updated_date":null,"description":null,"title":"Understanding JSONP","tags":["JavaScript","JSONP","API"],"pinned":null,"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.834862385321101,"src":"/static/85e9390b35cfcd06b4e455bd05cc8ea7/50fff/Screenshot-2018-06-29-12.21.27.webp","srcSet":"/static/85e9390b35cfcd06b4e455bd05cc8ea7/61e93/Screenshot-2018-06-29-12.21.27.webp 200w,\n/static/85e9390b35cfcd06b4e455bd05cc8ea7/1f5c5/Screenshot-2018-06-29-12.21.27.webp 400w,\n/static/85e9390b35cfcd06b4e455bd05cc8ea7/50fff/Screenshot-2018-06-29-12.21.27.webp 730w","sizes":"(max-width: 730px) 100vw, 730px"}}},"author":{"id":"Rakesh Soni","github":"oyesoni","avatar":"rakesh-soni.webp"}}}},{"node":{"excerpt":"NuGet is a free and open-source package manager for the .NET ecosystem. We can create and install packages using NuGet client tools. All of…","fields":{"slug":"/engineering/using-nuget-to-publish-net-packages/"},"html":"<p>NuGet is a free and open-source package manager for the .NET ecosystem. We can create and install packages using NuGet client tools. All of the .NET packages are hosted for publishing and consumption on a central package repository known as NuGet Gallery.</p>\n<p>Prerequisites</p>\n<ul>\n<li>Visual Studio 2017 (with .NET-related workload)</li>\n<li>nuget.exe (add it’s location to PATH environment variable)</li>\n<li>Valid account on nuget.org</li>\n</ul>\n<p><strong>Create a class library project</strong></p>\n<p>For a .NET package to be published in the NuGet Gallery, it should be a valid class library project. The following instructions can be used to create a simple class library project:</p>\n<ul>\n<li>Open Visual Studio, go to File > New > Project, expand the Visual C# > .NET Standard node. Select the \"Class Library (.NET Standard)\" template and provide a valid name.</li>\n<li>To build the project, right-click on the project file and select Build. A DLL file will be generated in the Debug folder or Release folder (if you build the configuration)</li>\n</ul>\n<p>For a real useful NuGet package, you should write necessary code which can be used by others to develop applications. However, a class library from the template is sufficient to create a package.</p>\n<p><strong>Configure Package Properties</strong></p>\n<ol>\n<li>Go to Project > Properties, select Package tab.</li>\n<li>Provide a unique identifier for your package and fill out other required properties. For a description of various properties, please visit <a href=\"https://docs.microsoft.com/en-us/nuget/reference/nuspec\">here</a>. The properties provided at this stage will be defined in .nuspec manifest that is created by Visual Studio for the project.</li>\n<li>To view the properties directly in the project file, right-click the project in Solution Explorer and select Edit AppLogger.csproj.</li>\n</ol>\n<p><strong>Run the pack command</strong></p>\n<ol>\n<li>Set the configuration to Release.</li>\n<li>Right click the project in Solution Explorer and select the Pack command.</li>\n<li>Visual Studio builds the project and creates the .nupkg file. Please note that the built package is in bin\\Release\\netstandard2.0 as befits the .NET Standard 2.0 target.</li>\n</ol>\n<p><strong>Acquire API Key</strong></p>\n<ol>\n<li><a href=\"https://www.nuget.org/users/account/LogOn?returnUrl=%2F\">Sign in to your nuget.org account</a> or create an account if it doesn’t already exist.</li>\n<li>Select your user name on the top right, then select API Keys.</li>\n<li>Select Create, provide a name for your key, select Select Scopes > Push.</li>\n<li>Under API Key, enter * for Glob pattern, then select Create.</li>\n<li>After the key is created, select Copy to retrieve the access key needed for publishing the package.</li>\n</ol>\n<p><strong>Important:</strong> Save your key in a secure location because you cannot copy the key again later on. If you return to the API key page, you need to regenerate the key to copy it.</p>\n<p><strong>Publish with nuget push</strong></p>\n<ol>\n<li>Open Command Prompt.</li>\n<li>Change to the folder containing the .nupkg file.</li>\n<li>\n<p>Run the following command, specifying your package name and replacing the key value with your API key:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"powershell\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk1\">nuget push &amp;lt;PACKAGE-NAME&amp;gt;.nupkg &amp;lt;API-KEY&amp;gt; -Source https://api.nuget.org/v3/index.json</span></span></code></pre>\n</li>\n<li>nuget.exe displays the results of the publishing process.</li>\n</ol>\n<p><strong>Manage the published package</strong></p>\n<p>You can view your published package in your profile on nuget.org. Select Manage Packages to see the one that was just published. It might take a while for your package to be visible in search results.</p>\n<p>If you want to unlist the package and hide it from search results, follow the steps listed below:</p>\n<ol>\n<li>On nuget.org, select your user name on top right, then select Manage Packages.</li>\n<li>Find the package to be unlisted under Published and select the trash can icon on the right.</li>\n<li>On the next page, clear the box labeled List (package-name) in search results and select Save.</li>\n</ol>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .dark-default-dark .mtk1 { color: #D4D4D4; }\n</style>","frontmatter":{"date":"June 28, 2018","updated_date":null,"description":null,"title":"Using NuGet to publish .NET packages","tags":["NuGet",".NET"],"pinned":null,"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1,"src":"/static/b965f64c315a9fddcd7037c0d1e1441f/ad85c/desdev.webp","srcSet":"/static/b965f64c315a9fddcd7037c0d1e1441f/61e93/desdev.webp 200w,\n/static/b965f64c315a9fddcd7037c0d1e1441f/1f5c5/desdev.webp 400w,\n/static/b965f64c315a9fddcd7037c0d1e1441f/ad85c/desdev.webp 600w","sizes":"(max-width: 600px) 100vw, 600px"}}},"author":{"id":"Hitesh Pamnani","github":null,"avatar":null}}}},{"node":{"excerpt":"Time Required : less than 10 minutes. Technologies : None. Prerequisites : None. This tutorial will go over how to configure the ‘Actions on…","fields":{"slug":"/engineering/how-to-configure-the-actions-on-google-console-for-google-assistant/"},"html":"<p><strong>Time Required :</strong> less than 10 minutes.</p>\n<p><strong>Technologies :</strong> None.</p>\n<p><strong>Prerequisites :</strong> None.</p>\n<p>This tutorial will go over how to configure the ‘Actions on Google’ console so that it can be used for your Google Assistant actions/events that can be accessed anywhere the Google Assistant is available from phones to Google Home products.</p>\n<h3 id=\"steps\" style=\"position:relative;\"><a href=\"#steps\" aria-label=\"steps permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Steps</strong></h3>\n<ol>\n<li>\n<p>Create an Actions project and a Dialogflow agent <a href=\"https://console.actions.google.com/u/0/\">here</a>.</p>\n<ol>\n<li>Add/import project.\n<img src=\"/ba196995f1e6e11edbc97407d537ac5e/Screen-Shot-2018-05-30-at-9.47.33-AM-300x280.webp\"></li>\n<li>Click on Skip(in the upper right corner) choosing a category and click Build -> Actions in the left nav to add your first actions.<br>\n<span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 300px; \"\n    >\n      <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 59.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url('data:image/webp;base64,UklGRm4AAABXRUJQVlA4IGIAAAAwBACdASoUAAwAPtFWo0uoJKMhsAgBABoJZACdIDf6AAeF4OYpdmQOgjAA/urzyRWMZSIXJgNyVPN+sSr6m8+jw2kOLf2pL6x2AXOEj5KNzRqpUqgTfWEQjvOOljv6wBgAAA=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Screen Shot 2018 05 30 at 10 02 46 AM 300x177\"\n        title=\"Screen Shot 2018 05 30 at 10 02 46 AM 300x177\"\n        src=\"/static/8ac47fc4a5ae215cff57c935e69a3557/c85cb/Screen-Shot-2018-05-30-at-10.02.46-AM-300x177.webp\"\n        srcset=\"/static/8ac47fc4a5ae215cff57c935e69a3557/c85cb/Screen-Shot-2018-05-30-at-10.02.46-AM-300x177.webp 300w\"\n        sizes=\"(max-width: 300px) 100vw, 300px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n    </span></li>\n</ol>\n</li>\n<li>\n<p>Configuration for your action intent in the dialogflow console.</p>\n<ol>\n<li>In the Intent section, enter your training phrases. Start with “talk to”, “speak to”, “ask” and etc, learn more <a href=\"https://developers.google.com/actions/localization/languages-locales\">here.</a>\n<img src=\"/330ce1c9b2fb89629d0215bb5f38d88f/Screen-Shot-2018-05-30-at-10.41.30-AM-300x288.webp\"></li>\n<li>Enter the default text response. This will be the default response for your action until you link your account with LoginRadius and build your customized response. (This will return errors if you leave this empty, even if you handle the response in your code.)<img src=\"/81328793b1a3955ed805c71a9c6664e7/Screen-Shot-2018-05-30-at-10.42.03-AM-300x193.webp\"></li>\n</ol>\n</li>\n</ol>\n<p>By then, you should have finished the setup for google assistant.</p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n</style>","frontmatter":{"date":"June 18, 2018","updated_date":null,"description":null,"title":"How to configure the 'Actions on Google' console for Google Assistant","tags":["Engineering","GoogleAssistant"],"pinned":null,"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.3333333333333333,"src":"/static/8744b7a7d1a64b8263651675a3ff555f/58556/google-home-max-13.webp","srcSet":"/static/8744b7a7d1a64b8263651675a3ff555f/61e93/google-home-max-13.webp 200w,\n/static/8744b7a7d1a64b8263651675a3ff555f/1f5c5/google-home-max-13.webp 400w,\n/static/8744b7a7d1a64b8263651675a3ff555f/58556/google-home-max-13.webp 800w,\n/static/8744b7a7d1a64b8263651675a3ff555f/99238/google-home-max-13.webp 1200w,\n/static/8744b7a7d1a64b8263651675a3ff555f/7c22d/google-home-max-13.webp 1600w","sizes":"(max-width: 800px) 100vw, 800px"}}},"author":{"id":"Vincent Lin","github":null,"avatar":null}}}},{"node":{"excerpt":"Improving quality of life for citizens is at the heart of many municipal digital initiatives. Even so, city leaders often choose to focus on…","fields":{"slug":"/identity/municipals-investing-citizen-engagement/"},"html":"<p>Improving quality of life for citizens is at the heart of many municipal digital initiatives. Even so, city leaders often choose to focus on improving their technology infrastructure without considering citizen engagement.</p>\n<p>A successful digital transformation can have many <a href=\"https://www.loginradius.com/resource/top-5-ways-to-improve-citizen-engagement-2/\">benefits for both citizens and municipalities</a>. But if cities do not put the energy into improving their citizen engagement, they will struggle to truly transform their communities.</p>\n<p>By investing in a well-designed and implemented citizen-centric solution, municipalities can achieve the following benefits.</p>\n<h3 id=\"1-increase-citizen-satisfaction-with-government-performance\" style=\"position:relative;\"><a href=\"#1-increase-citizen-satisfaction-with-government-performance\" aria-label=\"1 increase citizen satisfaction with government performance permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1. Increase citizen satisfaction with government performance</h3>\n<p><img src=\"/1a646b5f77388415e2621a5d5cec5ca8/collaboration-colleagues-community-2.webp\"></p>\n<p>Citizens want to engage with their local government. They want to be able to share their views and access channels to participate in the decisions of government, connect with representatives, and be assured that their input has meaning and relevance.</p>\n<p>Citizens have been very vocal about these methods of engagement, so it should come as no surprise that communities are happiest when their citizens feel well-informed about the municipality's business and have opportunities to be part of policy formation.</p>\n<h3 id=\"2-enhance-citizen-trust-in-civic-institutions\" style=\"position:relative;\"><a href=\"#2-enhance-citizen-trust-in-civic-institutions\" aria-label=\"2 enhance citizen trust in civic institutions permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2. Enhance citizen trust in civic institutions</h3>\n<p><img src=\"/96aff7164972798b815b07b2ed7b0433/berlin-building-business-3.webp\"></p>\n<p>The public has been demanding more transparency and accountability from government for years. These demands are prompting municipalities to change how they engage with the public.</p>\n<p>Especially in the era of media and \"fake news,\" it has never been more important for municipalities to have a direct way of engaging their citizens. Organizations need to ensure that the facts are correctly communicated, but also need to listen carefully to the public's voices.</p>\n<p>Municipalities need to have a direct line of engagement between themselves and the community instead of relying on the media to connect them. Otherwise, they will struggle to build trust. The more involved and accurately informed a community is, the more likely citizens will trust the institution that is engaging with them. </p>\n<h3 id=\"3-get-the-most-out-of-your-digital-solutions-which-saves-an-enormous-amount-of-time-and-money\" style=\"position:relative;\"><a href=\"#3-get-the-most-out-of-your-digital-solutions-which-saves-an-enormous-amount-of-time-and-money\" aria-label=\"3 get the most out of your digital solutions which saves an enormous amount of time and money permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3. Get the most out of your digital solutions, which saves an enormous amount of time and money</h3>\n<p><img src=\"/ea0f43721710974145272e12366a302e/advice-advise-advisor-4.webp\"></p>\n<p>Engaging the community to keep them better informed about what is going on will only help in the long run. For example, instead of spending too much time and resources on projects that are only going to get protested, why not get community input from the beginning? If citizens are more involved in the process, municipalities can reduce lengthy and expensive legal appeals as well.</p>\n<h2 id=\"how-to-improve-citizen-engagement\" style=\"position:relative;\"><a href=\"#how-to-improve-citizen-engagement\" aria-label=\"how to improve citizen engagement permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>How to Improve Citizen Engagement</h2>\n<p>Many municipalities are now undergoing <a href=\"https://www.loginradius.com/blog/identity/customer-experience-driving-digital-transformations/\">digital transformations</a> in order to improve transparency and communication. After all, with a more sophisticated digital ecosystem, you will be able to simplify how you interact with your citizens.</p>\n<p>Undergoing a digital transformation, however, is just one of the much-needed steps. In order to truly engage your citizens, you need to know who they are. To learn who they are, you have to incorporate digital identity into your transformation. </p>\n<p>How does digital identity help with engagement? In our new e-book, we have listed the top 5 ways to improve your citizen engagement, which include creating a seamless digital service by unifying citizen services and identity, as well as improving your privacy and security efforts. You can check out the new e-book <a href=\"https://www.loginradius.com/resource/top-5-ways-to-improve-citizen-engagement-2/\">here</a>. <img src=\"/0a80e2261e1690438d02b5fb6d35513d/Ebook-thumb.webp\"></p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n</style>","frontmatter":{"date":"June 18, 2018","updated_date":null,"description":"Municipalities can achieve many benefits by investing in a well-designed and implemented citizen-centered approach, while enhancing the quality of life for people is at the core of many digital municipal initiatives.","title":"Why Municipalities Are Investing in Citizen Engagement","tags":["public-sector"],"pinned":null,"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.4705882352941178,"src":"/static/211730b2addbb6996b0be156bf8569a8/58556/collaboration-colleagues-community-398532-1.webp","srcSet":"/static/211730b2addbb6996b0be156bf8569a8/61e93/collaboration-colleagues-community-398532-1.webp 200w,\n/static/211730b2addbb6996b0be156bf8569a8/1f5c5/collaboration-colleagues-community-398532-1.webp 400w,\n/static/211730b2addbb6996b0be156bf8569a8/58556/collaboration-colleagues-community-398532-1.webp 800w,\n/static/211730b2addbb6996b0be156bf8569a8/99238/collaboration-colleagues-community-398532-1.webp 1200w,\n/static/211730b2addbb6996b0be156bf8569a8/90fb1/collaboration-colleagues-community-398532-1.webp 1500w","sizes":"(max-width: 800px) 100vw, 800px"}}},"author":{"id":"Rakesh Soni","github":"oyesoni","avatar":"rakesh-soni.webp"}}}},{"node":{"excerpt":"Time Required: 20 minutes. Technologies: Express, Node.js, JavaScript. Prerequisites: Basic knowledge of Express, Node.js, and JavaScript…","fields":{"slug":"/engineering/creating-a-google-hangout-bot-with-express-and-node-js/"},"html":"<p>Time Required: 20 minutes.<br>\nTechnologies: Express, Node.js, JavaScript.<br>\nPrerequisites:</p>\n<ul>\n<li>Basic knowledge of Express, Node.js, and JavaScript.</li>\n<li>Node.js and npm are installed.</li>\n</ul>\n<p>This tutorial will go over how to build a bot that will respond to pings (i.e. @<bot-name>), and send messages to a chat room. On a high level, the bot will run on an express server, and receive pings via an HTTP endpoint. Responses to pings will be sent synchronously through a payload in the HTTP response, while bot-initiated messages will be sent asynchronously using the Google Hangout Chat API.</p>\n<h5 id=\"outline\" style=\"position:relative;\"><a href=\"#outline\" aria-label=\"outline permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Outline</strong></h5>\n<ol>\n<li>Environment setup.</li>\n<li>Get bot to respond to pings.</li>\n<li>Send bot-initiated messages.</li>\n<li>Deploy.</li>\n</ol>\n<h5 id=\"environment-setup\" style=\"position:relative;\"><a href=\"#environment-setup\" aria-label=\"environment setup permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Environment Setup</strong></h5>\n<p>Create a new project with the file ‘app.js’.<br>\nOpen command line/terminal, and navigate to your project directory. Run ‘npm init’, and press enter until package.json is created. Next, install the following dependencies:</p>\n<ul>\n<li>express: <code>npm install express --save</code></li>\n<li>body-parser:  <code>npm install body-parser --save</code></li>\n<li>googleapis: <code>npm install googleapis --save</code></li>\n<li>unirest:  <code>npm install unirest --save</code></li>\n</ul>\n<p>In ‘app.js’, let’s setup our server:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"js\" data-index=\"0\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">express</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">require</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;express&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">bodyParser</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">require</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;body-parser&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> { </span><span class=\"mtk12\">google</span><span class=\"mtk1\"> } = </span><span class=\"mtk11\">require</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;googleapis&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">app</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">express</span><span class=\"mtk1\">();</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">app</span><span class=\"mtk1\">.</span><span class=\"mtk11\">use</span><span class=\"mtk1\">(</span><span class=\"mtk12\">bodyParser</span><span class=\"mtk1\">.</span><span class=\"mtk11\">urlencoded</span><span class=\"mtk1\">({</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk12\">extended:</span><span class=\"mtk1\"> </span><span class=\"mtk4\">false</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}));</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">app</span><span class=\"mtk1\">.</span><span class=\"mtk11\">use</span><span class=\"mtk1\">(</span><span class=\"mtk12\">bodyParser</span><span class=\"mtk1\">.</span><span class=\"mtk11\">json</span><span class=\"mtk1\">());</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">app</span><span class=\"mtk1\">.</span><span class=\"mtk11\">listen</span><span class=\"mtk1\">(</span><span class=\"mtk7\">8100</span><span class=\"mtk1\">, </span><span class=\"mtk4\">function</span><span class=\"mtk1\">() {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk10\">console</span><span class=\"mtk1\">.</span><span class=\"mtk11\">log</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;App listening on port 8100.&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">});</span></span></code></pre>\n<p>Running ‘node app.js’ will now create a local server on port 8100.</p>\n<h5 id=\"responding-to-pings\" style=\"position:relative;\"><a href=\"#responding-to-pings\" aria-label=\"responding to pings permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Responding to Pings</strong></h5>\n<p>The bot will respond to pings through a HTTP POST endpoint. Create one with express:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"js\" data-index=\"1\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk12\">app</span><span class=\"mtk1\">.</span><span class=\"mtk11\">post</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;/&#39;</span><span class=\"mtk1\">, </span><span class=\"mtk4\">function</span><span class=\"mtk1\">(</span><span class=\"mtk12\">req</span><span class=\"mtk1\">, </span><span class=\"mtk12\">res</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk10\">console</span><span class=\"mtk1\">.</span><span class=\"mtk11\">log</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;someone pinged @&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk15\">if</span><span class=\"mtk1\"> (</span><span class=\"mtk12\">req</span><span class=\"mtk1\">.</span><span class=\"mtk12\">body</span><span class=\"mtk1\">.</span><span class=\"mtk12\">type</span><span class=\"mtk1\"> === </span><span class=\"mtk8\">&#39;MESSAGE&#39;</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk15\">return</span><span class=\"mtk1\"> </span><span class=\"mtk12\">res</span><span class=\"mtk1\">.</span><span class=\"mtk11\">json</span><span class=\"mtk1\">({</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      </span><span class=\"mtk12\">text:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&#39;sleeping...&#39;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    });</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  }</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">});</span></span></code></pre>\n<p>The bot will respond with the text: ‘sleeping…’.</p>\n<p>Synchronously responding to messages simply requires us to return a response to Google. The downside to this is the 30 second time limit before Google no longer accepts responses to the request. For instance, this would be a problem if you were building some kind of reminder app; the bot wouldn't be able to synchronously respond after 30 seconds. This is where async responses come in.</p>\n<h5 id=\"bot-initiated-messages\" style=\"position:relative;\"><a href=\"#bot-initiated-messages\" aria-label=\"bot initiated messages permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Bot-initiated Messages</strong></h5>\n<p>To show this, we will have our bot post to a chat room every 1 minute.</p>\n<p>Sending async messages to Google API requires a Service Account for authentication. Once authenticated, we can make a POST request to a Google API URL that will create a message.</p>\n<p>So first, create a Google Service Account following these <a href=\"https://developers.google.com/hangouts/chat/how-tos/service-accounts\">steps</a>. Take the downloaded JSON file and put it in the root directory of your project. Here, we renamed it to googlekeys.json:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"js\" data-index=\"2\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">gkeys</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">require</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;./googlekeys.json&#39;</span><span class=\"mtk1\">);</span></span></code></pre>\n<p>We will be making POST requests using Unirest:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"js\" data-index=\"3\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">unirest</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">require</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;unirest&#39;</span><span class=\"mtk1\">);</span></span></code></pre>\n<p>Now generate a JWT that will be used in our POST request:</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"js\" data-index=\"4\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">function</span><span class=\"mtk1\"> </span><span class=\"mtk11\">getJWT</span><span class=\"mtk1\">() {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk15\">return</span><span class=\"mtk1\"> </span><span class=\"mtk4\">new</span><span class=\"mtk1\"> </span><span class=\"mtk10\">Promise</span><span class=\"mtk1\">(</span><span class=\"mtk4\">function</span><span class=\"mtk1\">(</span><span class=\"mtk12\">resolve</span><span class=\"mtk1\">, </span><span class=\"mtk12\">reject</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk4\">let</span><span class=\"mtk1\"> </span><span class=\"mtk12\">jwtClient</span><span class=\"mtk1\"> = </span><span class=\"mtk4\">new</span><span class=\"mtk1\"> </span><span class=\"mtk10\">google</span><span class=\"mtk1\">.</span><span class=\"mtk10\">auth</span><span class=\"mtk1\">.</span><span class=\"mtk10\">JWT</span><span class=\"mtk1\">(</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      </span><span class=\"mtk12\">gkeys</span><span class=\"mtk1\">.</span><span class=\"mtk12\">client_email</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      </span><span class=\"mtk4\">null</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      </span><span class=\"mtk12\">gkeys</span><span class=\"mtk1\">.</span><span class=\"mtk12\">private_key</span><span class=\"mtk1\">, [</span><span class=\"mtk8\">&#39;https://www.googleapis.com/auth/chat.bot&#39;</span><span class=\"mtk1\">]</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    );</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    </span><span class=\"mtk12\">jwtClient</span><span class=\"mtk1\">.</span><span class=\"mtk11\">authorize</span><span class=\"mtk1\">(</span><span class=\"mtk4\">function</span><span class=\"mtk1\">(</span><span class=\"mtk12\">err</span><span class=\"mtk1\">, </span><span class=\"mtk12\">tokens</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      </span><span class=\"mtk15\">if</span><span class=\"mtk1\"> (</span><span class=\"mtk12\">err</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        </span><span class=\"mtk10\">console</span><span class=\"mtk1\">.</span><span class=\"mtk11\">log</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;Error create JWT hangoutchat&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        </span><span class=\"mtk11\">reject</span><span class=\"mtk1\">(</span><span class=\"mtk12\">err</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      } </span><span class=\"mtk15\">else</span><span class=\"mtk1\"> {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">        </span><span class=\"mtk11\">resolve</span><span class=\"mtk1\">(</span><span class=\"mtk12\">tokens</span><span class=\"mtk1\">.</span><span class=\"mtk12\">access_token</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      }</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">    });</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  });</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>Here is our function for posting messages. ROOM-ID can be found in the URL of the hangout chat room page i.e.: <code>https://chat.google.com/u/0/room/{ROOM-ID}</code></p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"js\" data-index=\"5\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">function</span><span class=\"mtk1\"> </span><span class=\"mtk11\">postMessage</span><span class=\"mtk1\">(</span><span class=\"mtk12\">count</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk15\">return</span><span class=\"mtk1\"> </span><span class=\"mtk4\">new</span><span class=\"mtk1\"> </span><span class=\"mtk10\">Promise</span><span class=\"mtk1\">(</span><span class=\"mtk4\">function</span><span class=\"mtk1\">(</span><span class=\"mtk12\">resolve</span><span class=\"mtk1\">, </span><span class=\"mtk12\">reject</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      </span><span class=\"mtk11\">getJWT</span><span class=\"mtk1\">().</span><span class=\"mtk11\">then</span><span class=\"mtk1\">(</span><span class=\"mtk4\">function</span><span class=\"mtk1\">(</span><span class=\"mtk12\">token</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">          </span><span class=\"mtk12\">unirest</span><span class=\"mtk1\">.</span><span class=\"mtk11\">post</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;https://chat.googleapis.com/v1/spaces/&#39;</span><span class=\"mtk1\"> + {ROOM-</span><span class=\"mtk12\">ID</span><span class=\"mtk1\">} + </span><span class=\"mtk8\">&#39;/messages&#39;</span><span class=\"mtk1\">)</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">              .</span><span class=\"mtk11\">headers</span><span class=\"mtk1\">({</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">                  </span><span class=\"mtk8\">&quot;Content-Type&quot;</span><span class=\"mtk12\">:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;application/json&quot;</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">                  </span><span class=\"mtk8\">&quot;Authorization&quot;</span><span class=\"mtk12\">:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&quot;Bearer &quot;</span><span class=\"mtk1\"> + </span><span class=\"mtk12\">token</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">              })</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">              .</span><span class=\"mtk11\">send</span><span class=\"mtk1\">(</span><span class=\"mtk10\">JSON</span><span class=\"mtk1\">.</span><span class=\"mtk11\">stringify</span><span class=\"mtk1\">({</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">                  </span><span class=\"mtk8\">&#39;text&#39;</span><span class=\"mtk12\">:</span><span class=\"mtk1\"> </span><span class=\"mtk8\">&#39;Hello! This is message number &#39;</span><span class=\"mtk1\"> + </span><span class=\"mtk12\">count</span><span class=\"mtk1\">,</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">              }))</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">              .</span><span class=\"mtk11\">end</span><span class=\"mtk1\">(</span><span class=\"mtk4\">function</span><span class=\"mtk1\">(</span><span class=\"mtk12\">res</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">                  </span><span class=\"mtk11\">resolve</span><span class=\"mtk1\">();</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">              });</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      }).</span><span class=\"mtk11\">catch</span><span class=\"mtk1\">(</span><span class=\"mtk4\">function</span><span class=\"mtk1\">(</span><span class=\"mtk12\">err</span><span class=\"mtk1\">) {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">          </span><span class=\"mtk11\">reject</span><span class=\"mtk1\">(</span><span class=\"mtk12\">err</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      });</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  });</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">}</span></span></code></pre>\n<p>Finally, add the code that will repeat our post every minute.</p>\n<pre class=\"grvsc-container dark-default-dark\" data-language=\"js\" data-index=\"6\"><code class=\"grvsc-code\"><span class=\"grvsc-line\"><span class=\"mtk4\">const</span><span class=\"mtk1\"> </span><span class=\"mtk12\">timer</span><span class=\"mtk1\"> = </span><span class=\"mtk11\">require</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;timers&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk12\">app</span><span class=\"mtk1\">.</span><span class=\"mtk11\">listen</span><span class=\"mtk1\">(</span><span class=\"mtk7\">8100</span><span class=\"mtk1\">, </span><span class=\"mtk4\">function</span><span class=\"mtk1\">() {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk10\">console</span><span class=\"mtk1\">.</span><span class=\"mtk11\">log</span><span class=\"mtk1\">(</span><span class=\"mtk8\">&#39;App listening on port 8100.&#39;</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk4\">let</span><span class=\"mtk1\"> </span><span class=\"mtk12\">count</span><span class=\"mtk1\"> = </span><span class=\"mtk7\">0</span><span class=\"mtk1\">;</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  </span><span class=\"mtk12\">timer</span><span class=\"mtk1\">.</span><span class=\"mtk11\">setInterval</span><span class=\"mtk1\">(</span><span class=\"mtk4\">function</span><span class=\"mtk1\">() {</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">      </span><span class=\"mtk11\">postMessage</span><span class=\"mtk1\">(</span><span class=\"mtk12\">count</span><span class=\"mtk1\"> += </span><span class=\"mtk7\">1</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">  }, </span><span class=\"mtk7\">60000</span><span class=\"mtk1\">);</span></span>\n<span class=\"grvsc-line\"><span class=\"mtk1\">});</span></span></code></pre>\n<h5 id=\"deploy\" style=\"position:relative;\"><a href=\"#deploy\" aria-label=\"deploy permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Deploy</strong></h5>\n<p>Expose your local server to public (we used <a href=\"https://ngrok.com/\">ngrok</a>).</p>\n<p>Login to <a href=\"https://console.developers.google.com\">developer console</a>. Create a new project, and enable Hangout Chat API. Under configuration, set:</p>\n<ul>\n<li>status: live</li>\n<li>bot name (this is how you will add and ping the bot)</li>\n<li>avatar</li>\n<li>description</li>\n<li>functionality: rooms</li>\n<li>connection settings - bot URL: <your ngrok HTTPS url></li>\n<li>permission: everyone in your domain</li>\n</ul>\n<p>Restart your local server, and that’s it! Make sure you have your bot added to the chat room, and you can ping it by sending @<bot-name>. The bot will also post to the chat room every minute.</p>\n<p>There are a lot of different ways to further extend this bot, such as setting reminders/notifications, making to-do lists, displaying server logs, and interacting with API’s.</p>\n<p><img src=\"/8a9a029d9f5b2717f8798fde837c34e0/image2.webp\"></p>\n<p><img src=\"/b8ded5c577b86826fa436a130babfd07/image1.webp\"></p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n  .dark-default-dark {\n    background-color: #1E1E1E;\n    color: #D4D4D4;\n  }\n  .dark-default-dark .mtk4 { color: #569CD6; }\n  .dark-default-dark .mtk1 { color: #D4D4D4; }\n  .dark-default-dark .mtk12 { color: #9CDCFE; }\n  .dark-default-dark .mtk11 { color: #DCDCAA; }\n  .dark-default-dark .mtk8 { color: #CE9178; }\n  .dark-default-dark .mtk7 { color: #B5CEA8; }\n  .dark-default-dark .mtk10 { color: #4EC9B0; }\n  .dark-default-dark .mtk15 { color: #C586C0; }\n</style>","frontmatter":{"date":"June 06, 2018","updated_date":null,"description":null,"title":"Creating a Google Hangout Bot with Express and Node.js","tags":["Express","NodeJs","Hangout"],"pinned":null,"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1,"src":"/static/ce69f8b38834ee73f9b8821830d73c87/0c543/hangoutchatimgage2.webp","srcSet":"/static/ce69f8b38834ee73f9b8821830d73c87/61e93/hangoutchatimgage2.webp 200w,\n/static/ce69f8b38834ee73f9b8821830d73c87/1f5c5/hangoutchatimgage2.webp 400w,\n/static/ce69f8b38834ee73f9b8821830d73c87/0c543/hangoutchatimgage2.webp 630w","sizes":"(max-width: 630px) 100vw, 630px"}}},"author":{"id":"Andy Yeung","github":null,"avatar":null}}}}]},"markdownRemark":{"excerpt":"Identity is evolving, and developers are at the forefront of this transformation. Every day brings a new learning—adapting to new standards…","fields":{"slug":"/identity/developer-first-identity-provider-loginradius/"},"html":"<p>Identity is evolving, and developers are at the forefront of this transformation. Every day brings a new learning—adapting to new standards and refining approaches to building secure, seamless experiences.</p>\n<p>We’re here to support developers on that journey. We know how important simplicity, efficiency, and well-structured documentation are when working with identity and access management solutions. That’s why we’ve redesigned the <a href=\"https://www.loginradius.com/\">LoginRadius website</a>—to be faster, more intuitive, and developer-first in every way.</p>\n<p>The goal? Having them spend less time searching and more time building.</p>\n<h2 id=\"whats-new-and-improved-on-the-loginradius-website\" style=\"position:relative;\"><a href=\"#whats-new-and-improved-on-the-loginradius-website\" aria-label=\"whats new and improved on the loginradius website permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>What’s New and Improved on the LoginRadius Website?</h2>\n<p>LoginRadius’ vision is to give developers a product that simplifies identity management so they can focus on building, deploying, and scaling their applications. To enhance this experience, we’ve spent the last few months redesigning our interface— making navigation more intuitive and reassuring that essential resources are easily accessible.</p>\n<p>Here’s a closer look at what’s new and why it’s important:</p>\n<h3 id=\"a-developer-friendly-dark-theme\" style=\"position:relative;\"><a href=\"#a-developer-friendly-dark-theme\" aria-label=\"a developer friendly dark theme permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>A Developer-Friendly Dark Theme</h3>\n<p><img src=\"/f46881583c7518a93bb24e94c32320de/a-developer-friendly-dark-theme.webp\" alt=\"This image shows how LoginRadius offers several authentication methods like traditional login, social login, passwordless login, passkeys and more in a dark mode.\">    </p>\n<p>Developers spend long hours working in dark-themed IDEs and terminals, so we’ve designed the LoginRadius experience to be developer-friendly and align with that preference.</p>\n<p>The new dark mode reduces eye strain, enhances readability, and provides a seamless transition between a coding environment and our platform. Our new design features a clean, modern aesthetic with a consistent color scheme and Barlow typography, ensuring better readability. High-quality graphics and icons are thoughtfully placed to enhance the content without adding visual clutter.</p>\n<p>So, whether you’re navigating our API docs or configuring authentication into your system, our improved interface will make those extended development hours more comfortable and efficient.</p>\n<h3 id=\"clear-categorization-for-loginradius-capabilities\" style=\"position:relative;\"><a href=\"#clear-categorization-for-loginradius-capabilities\" aria-label=\"clear categorization for loginradius capabilities permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Clear Categorization for LoginRadius Capabilities</h3>\n<p><img src=\"/e5358b82be414940f3fb146013845933/capabilities.webp\" alt=\"This image shows a breakdown of all the LoginRadius CIAM capabilities, including authentication, security, UX, scalability and multi-brand management.\"></p>\n<p>We’ve restructured our website to provide a straightforward breakdown of our customer identity and access management platform capabilities, helping you quickly find what you need:</p>\n<ul>\n<li>Authentication: Easily understand <a href=\"https://www.loginradius.com/blog/identity/authentication-option-for-your-product/\">how to choose the right login method</a>, from traditional passwords and OTPs to social login, federated SSO, and passkeys with few lines of code.</li>\n<li>Security: Implement no-code security features like bot detection, IP throttling, breached password alerts, DDoS protection, and adaptive MFA to safeguard user accounts.</li>\n<li>User Experience: Leverage AI builder, hosted pages, and drag-and-drop workflows to create smooth, branded sign-up and login experiences.</li>\n<li>High Performance &#x26; Scalability: Confidently scale with sub-100ms API response times, 100% uptime, 240K+ RPS, and 28+ global data center regions.</li>\n<li>Multi-Brand Management: Efficiently manage multiple identity apps, choosing isolated or shared data stores based on your brand’s unique needs.</li>\n</ul>\n<p>This structured layout ensures you can quickly understand each capability and how it integrates into your identity ecosystem.</p>\n<h3 id=\"developer-first-navigation\" style=\"position:relative;\"><a href=\"#developer-first-navigation\" aria-label=\"developer first navigation permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Developer-First Navigation</h3>\n<p><img src=\"/a8c155c2b6faf3d5f4b4de4e2b14d763/developers-menu.webp\" alt=\"This image shows the LoginRadius menu bar, highlighting the developer dropdown.\">   </p>\n<p>We’ve been analyzing developer workflows to identify how you access key resources. That’s why we redesigned our navigation with one goal in mind: to reduce clicks and make essential resources readily available.</p>\n<p>The new LoginRadius structure puts APIs, SDKs, and integration guides right at the menu bar under the Developers dropdown so you can get started faster. Our Products, Solutions, and Customer Services are also clearly categorized, helping development teams quickly find the right tools and make informed decisions.</p>\n<h3 id=\"quick-understanding-of-integration-benefits\" style=\"position:relative;\"><a href=\"#quick-understanding-of-integration-benefits\" aria-label=\"quick understanding of integration benefits permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Quick Understanding of Integration Benefits</h3>\n<p><img src=\"/b2f9a964a2da0ea83e2f8596b833bba7/we-support-your-tech-stack.webp\" alt=\"This image shows a list of popular programming languages and frameworks offered by LoginRadius.\"></p>\n<p>Developers now have a clear view of the tech stack available with LoginRadius, designed to support diverse business needs.</p>\n<p>Our platform offers pre-built SDKs for Node.js, Python, Java, and more, making CIAM integration seamless across popular programming languages and frameworks.</p>\n<h2 id=\"over-to-you-now\" style=\"position:relative;\"><a href=\"#over-to-you-now\" aria-label=\"over to you now permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Over to You Now!</h2>\n<p>Check out our <a href=\"https://www.loginradius.com/\">revamped LoginRadius website</a> and see how the improved experience makes it easier to build, scale, and secure your applications.</p>\n<p>Do not forget to explore the improved navigation and API documentation, and get started with our free trial today. We’re excited to see what you’ll build with LoginRadius!</p>\n<style class=\"grvsc-styles\">\n  .grvsc-container {\n    overflow: auto;\n    -webkit-overflow-scrolling: touch;\n    padding-top: 1rem;\n    padding-top: var(--grvsc-padding-top, var(--grvsc-padding-v, 1rem));\n    padding-bottom: 1rem;\n    padding-bottom: var(--grvsc-padding-bottom, var(--grvsc-padding-v, 1rem));\n    border-radius: 8px;\n    border-radius: var(--grvsc-border-radius, 8px);\n    font-feature-settings: normal;\n  }\n  \n  .grvsc-code {\n    display: inline-block;\n    min-width: 100%;\n  }\n  \n  .grvsc-line {\n    display: inline-block;\n    box-sizing: border-box;\n    width: 100%;\n    padding-left: 1.5rem;\n    padding-left: var(--grvsc-padding-left, var(--grvsc-padding-h, 1.5rem));\n    padding-right: 1.5rem;\n    padding-right: var(--grvsc-padding-right, var(--grvsc-padding-h, 1.5rem));\n  }\n  \n  .grvsc-line-highlighted {\n    background-color: var(--grvsc-line-highlighted-background-color, transparent);\n    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, transparent);\n  }\n  \n</style>","frontmatter":{"date":"February 21, 2025","updated_date":null,"description":"LoginRadius’ vision is to give developers a product that simplifies identity management so they can focus on building, deploying, and scaling their applications. To enhance this experience, we’ve redesigned our website interface, making navigation more intuitive and reassuring that essential resources are easily accessible.","title":"Revamped & Ready: Introducing the New Developer-First LoginRadius Website","tags":["Developer tools","API","Identity Management","User Authentication"],"pinned":true,"coverImage":{"childImageSharp":{"fluid":{"aspectRatio":1.7857142857142858,"src":"/static/80b4e4fbe176a10a327d273504607f32/58556/hero-section.webp","srcSet":"/static/80b4e4fbe176a10a327d273504607f32/61e93/hero-section.webp 200w,\n/static/80b4e4fbe176a10a327d273504607f32/1f5c5/hero-section.webp 400w,\n/static/80b4e4fbe176a10a327d273504607f32/58556/hero-section.webp 800w,\n/static/80b4e4fbe176a10a327d273504607f32/99238/hero-section.webp 1200w,\n/static/80b4e4fbe176a10a327d273504607f32/7c22d/hero-section.webp 1600w,\n/static/80b4e4fbe176a10a327d273504607f32/1258b/hero-section.webp 2732w","sizes":"(max-width: 800px) 100vw, 800px"}}},"author":{"id":"Rakesh Soni","github":"oyesoni","avatar":"rakesh-soni.webp"}}}},"pageContext":{"limit":6,"skip":882,"currentPage":148,"type":"///","numPages":164,"pinned":"ee8a4479-3471-53b1-bf62-d0d8dc3faaeb"}},"staticQueryHashes":["1171199041","1384082988","2100481360","23180105","528864852"]}