<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: vidi42</title>
    <description>The latest articles on DEV Community by vidi42 (@vidi42).</description>
    <link>https://dev.to/vidi42</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F160780%2F6e7a0173-e5df-48c2-b4ef-1eb17e19e6ea.jpg</url>
      <title>DEV Community: vidi42</title>
      <link>https://dev.to/vidi42</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vidi42"/>
    <language>en</language>
    <item>
      <title>Keycloak one-time link for resource access</title>
      <dc:creator>vidi42</dc:creator>
      <pubDate>Sat, 01 Feb 2020 17:15:16 +0000</pubDate>
      <link>https://dev.to/vidi42/keycloak-one-time-resource-access-9df</link>
      <guid>https://dev.to/vidi42/keycloak-one-time-resource-access-9df</guid>
      <description>&lt;h2&gt;
  
  
  The premise
&lt;/h2&gt;

&lt;p&gt;Recently in one of the applications we are building, we wanted to give the users the possibility to create a one-time access URL for a resource, which they can then share with another user.&lt;/p&gt;

&lt;h2&gt;
  
  
  The context
&lt;/h2&gt;

&lt;p&gt;We are building web-based applications, with an Angular frontend and a Java backend.&lt;br&gt;
Our authorization is handled by Keycloak.&lt;/p&gt;
&lt;h2&gt;
  
  
  The success criteria
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The link can be easily shared through any mean, typing or QR, so it cannot be extremely long&lt;/li&gt;
&lt;li&gt;The link should only give access to that one user (consumer) and one resource and cannot be used by other users to access the same resource&lt;/li&gt;
&lt;li&gt;And of course, the consumer user doesn't need to type his/her credentials to access the resources trough the received link&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;This is, of course, a stripped-down version of the final solution, meant to be used as a guideline.&lt;/p&gt;
&lt;h3&gt;
  
  
  Frontend part
&lt;/h3&gt;

&lt;p&gt;Since we are using Angular on the frontend side we already had integrated &lt;a href="https://www.npmjs.com/package/keycloak-angular"&gt;keycloak-angular&lt;/a&gt; so not much work to be done there.&lt;/p&gt;
&lt;h3&gt;
  
  
  Backend side
&lt;/h3&gt;

&lt;p&gt;This is where all the heavy lifting was done for the whole of the implementation.&lt;/p&gt;

&lt;p&gt;There are two main entry points into the solution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trough an API endpoint for generating the one-time access URL for the consumer user and the resource&lt;/li&gt;
&lt;li&gt;Trough the one-time access URL which points the right user to the right resource&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  1. Generating the one-time access URL
&lt;/h4&gt;

&lt;p&gt;Nothing fancy here in regards to the API endpoint implementation, except maybe that is a REST endpoint.&lt;/p&gt;

&lt;p&gt;So something like this: &lt;code&gt;POST /oneTimeUrl/{consumerUserId}/{resourceId}&lt;/code&gt; and of course which is secured with an &lt;code&gt;Authorization&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;Having such an endpoint then gives you all the information you need to create the one-time access URL.&lt;/p&gt;

&lt;p&gt;Before we get to the one-time access URL, we need to find a way to allow the consumer user to be directly authenticated when accessing the URL (remember the success criteria). For that, we will need to get a hold of an access token for him.&lt;/p&gt;

&lt;p&gt;In order to do that, I used one of Keycloak's not so known features, but an OAuth 2.0 standard &lt;a href="https://tools.ietf.org/html/rfc8693"&gt;Token Exchange&lt;/a&gt;.&lt;br&gt;
&lt;em&gt;Keycloak documentation &lt;a href="https://www.keycloak.org/docs/latest/securing_apps/#_token-exchange"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And it looks something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;HttpPost&lt;/span&gt; &lt;span class="n"&gt;httpPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpPost&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keycloakBaseUrl&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/realms/MyRealm/protocol/openid-connect/token"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BasicNameValuePair&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BasicNameValuePair&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resourceClientId&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BasicNameValuePair&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"grant_type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"urn:ietf:params:oauth:grant-type:token-exchange"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BasicNameValuePair&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"subject_token"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myAccessToken&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BasicNameValuePair&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"requested_subject"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keycloakConsumerUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;

&lt;span class="nc"&gt;UrlEncodedFormEntity&lt;/span&gt; &lt;span class="n"&gt;formEntity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UrlEncodedFormEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;StandardCharsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UTF_8&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;httpPost&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formEntity&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;CloseableHttpResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpPost&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 &lt;em&gt;your current user will have to have the impersonation role for this to work&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And we will get the same response as when the authorization is concluded which contains an &lt;code&gt;accessToken&lt;/code&gt;, &lt;code&gt;refreshToken&lt;/code&gt; and other access token related data.&lt;/p&gt;

&lt;p&gt;So using the consumer user's accessToken we just obtained and having the resourceId we can now store the pair and build a unique URL that points to them.&lt;/p&gt;

&lt;p&gt;Something like &lt;code&gt;GET /sharableUrl/{uniqueIdentifier}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And with this we have our first 2 points from the success criteria covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;link is short and can be easily shared ✔️&lt;/li&gt;
&lt;li&gt;link points to a certain user and a resource ✔️&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Accessing the one-time URL
&lt;/h4&gt;

&lt;p&gt;Now that we have our one-time sharable URL which points to a valid consumer user access token and a resource we are halfway there because this is just a more friendly gateway for the user to use, but it has to do some background work as well.&lt;/p&gt;

&lt;p&gt;And what the servlet behind this URL does is basically build a bit uglier URL which redirects the user to the resource he needs to access.&lt;/p&gt;

&lt;p&gt;So basically using the identifier we can search for an access token and a resource in our storing system and if we find them we can begin constructing the real one-time access URL.&lt;/p&gt;

&lt;p&gt;We will end-up with something like this &lt;code&gt;GET /resourceUrl?accessToken={consumerUserAccessToken}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is uglier because if the access token is a JWT it will be a pretty long one.&lt;/p&gt;

&lt;p&gt;But now, we have everything we need to redirect the user to the resource. &lt;br&gt;
Looking at the success criteria it feels like we the last point as well, except that it won't really work as expected and the consumer user will still be required to input his/her credentials.&lt;/p&gt;

&lt;p&gt;That's because the &lt;strong&gt;keycloak-angular&lt;/strong&gt; package configured on the fronted side does not accept the access token as a query parameter so we will be redirected to the login screen.&lt;/p&gt;
&lt;h3&gt;
  
  
  Keycloak side
&lt;/h3&gt;

&lt;p&gt;So yeah the Keycloak shares on that heavy lifting as well.&lt;/p&gt;

&lt;p&gt;In order to login the user directly we will try and stop Keycloak from showing the login screen if it notices there is a valid access token somewhere in the redirect URL.&lt;/p&gt;

&lt;p&gt;For that, we configured a new Browser Flow similar to the default one, but with an extra step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bMaXpeYl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h6d2an28lz8bww1ueoyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bMaXpeYl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/h6d2an28lz8bww1ueoyc.png" alt="copy of browser flow"&gt;&lt;/a&gt;&lt;br&gt;
💡 &lt;em&gt;the new Access Token URI Flow will try to execute before the normal login form flow as an alternative login&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the script execution, we will try and validate the access token from the URL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getAccessToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;decodedAccessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AccessToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;decodedAccessToken&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;decodedAccessToken&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Missing access token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempted&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decodedAccessToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPreferredUsername&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getUserByUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getRealm&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;LOG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No user found for username &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attempted&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAuthenticationSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setRedirectUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cleanTheAccessTokenFromTheRedirectUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A rundown of the script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we get the access token from the URL&lt;/li&gt;
&lt;li&gt;we try to decode it, which also runs a validation check on it to make sure is not expired or tempered with&lt;/li&gt;
&lt;li&gt;we get a user based on the username from the access token and log it in&lt;/li&gt;
&lt;li&gt;we cleanup the redirect URL of the access token parameter before redirecting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And with that, we have our latest success criteria point in place: not requiring the user to input his/her credentials ✔️.&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>oauth</category>
      <category>authorization</category>
      <category>web</category>
    </item>
    <item>
      <title>Going to GOTO Berlin</title>
      <dc:creator>vidi42</dc:creator>
      <pubDate>Tue, 26 Nov 2019 17:58:18 +0000</pubDate>
      <link>https://dev.to/vidi42/going-to-goto-berlin-41n1</link>
      <guid>https://dev.to/vidi42/going-to-goto-berlin-41n1</guid>
      <description>&lt;p&gt;Have you ever been to Berlin, or to a tech conference in Berlin, or at least curious how a tech conference in Berlin would be? Well I’m here to help you with the last one and hopefully, in the process, help you decide on the first two.&lt;/p&gt;

&lt;p&gt;That’s because I have spent 4 days in Berlin, 3 of which were at the &lt;a href="https://gotober.com/"&gt;GOTO &lt;/a&gt; conference and I would like to share with you my experience, through some of the bestofs from the conference.&lt;/p&gt;

&lt;p&gt;Before we dive right into them, I would like to make a few disclaimers: There were a lot of cool presentation, but I won’t have time to go through each so I hand picked those which represent different aspects of our working life. And second, I invite you to also have a look at the presentations by yourself and make you own opinion about the subjects presented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting there
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qlcJOZA2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bcqi3jjhpaiil5qdg7dn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qlcJOZA2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bcqi3jjhpaiil5qdg7dn.jpg" alt="Berling Airport"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My plane landed in Berlin just 2 hours before the opening Keynote, but since there were no delays, thanks to Berlin’s great S-Bahn system and a little bit of planning ahead, not only I reached the conference in time, but I also managed to check-in at the hotel, grab a coffee and even some breakfast before the Keynote.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with a laugh
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s4EzbSPJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e243htpyd9pdnf6zqivi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s4EzbSPJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e243htpyd9pdnf6zqivi.jpg" alt="Conference Banner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Starting off on the right foot, what more could I ask for that day.&lt;/p&gt;

&lt;p&gt;Well, everything is better if you start with a laugh, isn’t it. Mostly everything. I’m sure a conference is one of those things. &lt;/p&gt;

&lt;p&gt;The opening keynote, The importance of Laughter, was held by Aino Vonge Corry, one of the funniest speakers I’ve seen on a stage, who set the tone for me for the rest of the conference, because I learned a lot while also having a laugh and basically that’s exactly why I went to a conference in the first place.&lt;/p&gt;

&lt;p&gt;For example I learned which is the funniest joke in the world, scientifically proven (exactly the content you don’t expect, but appreciate at a tech conference). Here’s the report, but I also recommend you watch Aino’s talk on &lt;a href="https://www.youtube.com/watch?v=WMv9glJuoGc"&gt;Youtube&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Mob programming
&lt;/h2&gt;

&lt;p&gt;&amp;gt;&amp;gt; Insert here expanding brain meme: Solo → Pair → Mob Programming &amp;lt;&amp;lt;&lt;/p&gt;

&lt;p&gt;I’ve been doing solo programming, heard and seen pair programming, but honestly I’ve never heard about mob programming (and it felt like I was the only one). That’s why Woody Zuill’s presentation was so great, because it managed to present me with a concept I’ve never heard of before and made me wanna try it out. For those who, as me, didn’t heard about mob programming before, the picture bellow should explain pretty much the whole concept.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5V0SJQPp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5e6de1fsrnljdforfwgg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5V0SJQPp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5e6de1fsrnljdforfwgg.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is pretty much my desk at least once a week and not because we’re doing mob programming.&lt;/p&gt;

&lt;p&gt;But the idea of Mob Programming is much more then fixing issues together, it’s actually implementing and delivering features like this. You have your Navigator or Navigators which come up with the ideas and next steps, then your Driver which is doing all the typing and you rotate so that everyone has the chance of being the Driver.&lt;/p&gt;

&lt;p&gt;The pros are that you have everyone focusing on the same issue instead of all going at different speeds, waiting one for another, asking around and wasting too much time on communication channels.&lt;/p&gt;

&lt;p&gt;As a personal  thought of this, I can see this working in most of the projects (even if the teams are remote) for smaller periods of time of or per feature (maybe a complex feature) better then the traditional solo programming.&lt;/p&gt;

&lt;p&gt;If the manager or programmer super star in you starts screaming, I feel you, but I also invite you to watch Woody Zuill’s presentation, because it addresses a lot of the questions your inner you has. (&lt;a href="https://gotober.com/2019/sessions/843/mob-programming-and-the-power-of-flow"&gt;https://gotober.com/2019/sessions/843/mob-programming-and-the-power-of-flow&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes
&lt;/h2&gt;

&lt;p&gt;Ah, the cool “new” kid on the block with lots of toys, which you see everywhere you go, it’s following you until you have no other option but befriend him.&lt;/p&gt;

&lt;p&gt;Even if it’s almost a decade old I’ve never had the chance to work with it, but given recent organizational changes and trends, it feels more like I’m on the brink of having to say “hi” pretty soon.&lt;/p&gt;

&lt;p&gt;And if that’s the case I wanted to find out what working with Kubernetes is really like by joining a talk which present you with a list of tools you could use in your daily work and what you could do with them. Straight forward, right? Right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--45WvS8sV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/us28gkm33luyxb7xu2s8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--45WvS8sV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/us28gkm33luyxb7xu2s8.jpg" alt="Kubernetes tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, sort of. Ellen Köber really made a good summary of each tool and it’s pros and cons (a list you can see in the picture above), but to be honest it was kind of overwhelming. There’s no right answer for anything and it all depends on your way of working and you own organizational constraints. So if your like me and you don’t already use Kubernetes it might be difficult to see how all the pieces fall together, but then again looking at the picture above you can see a pretty traditional CI pipeline there.&lt;/p&gt;

&lt;p&gt;If you are using already Kubernetes then I suggest you take a look at Ellen’s talk, you might find out about some tools you didn’t know and could use right away. If you are not already using Kubernetes, it’s just a matter of time until you will, so you might also want to check it so you can put some aces up your sleeves.&lt;/p&gt;

&lt;p&gt;(&lt;a href="https://gotober.com/2019/sessions/821/kubernetes-day-3-the-state-of-kubernetes-development-tooling"&gt;https://gotober.com/2019/sessions/821/kubernetes-day-3-the-state-of-kubernetes-development-tooling&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Extreme Digitalization in China
&lt;/h2&gt;

&lt;p&gt;If somebody would to ask me to explain to them how does extreme digitalization really manifests in China, I would tell them this: “Smile to the camera for 60 cm of toilet paper. Oh, and really fast trains.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BGM6aM0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fj7j5bhs6lkbt0pmmcog.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BGM6aM0p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fj7j5bhs6lkbt0pmmcog.jpg" alt="Toilet Paper Smile powered vending machine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe that’s the answer you were waiting for. Of course all the room started laughing when we heard Christina Boutrup present this example, but then it dawned on me, that’s one of the best examples for Extreme Digitalization. Because it’s not only about having you own Facebook, Whatsapp and Shopping app into one, or you own internet. It’s not event about fast trains (.. ok, it’s a bit also about having the fastest trains), but mostly it’s about digitalizing every basic aspect of you life in such a way that you have to trade you personal data for even the most mundane things.&lt;/p&gt;

&lt;p&gt;During the Keynote Christina went into many details about what this means for the people of China and also for the rest of the world, because we must not forget, China has one of the biggest consumer market and everybody wants a piece of it. &lt;/p&gt;

&lt;p&gt;I would like to invite you to watch her entertaining and eye opening presentation. I’ve also added her book to my Goodreads Want to Read list.&lt;/p&gt;

&lt;p&gt;(&lt;a href="https://gotober.com/2019/sessions/1061/extreme-digitalization-in-china"&gt;https://gotober.com/2019/sessions/1061/extreme-digitalization-in-china&lt;/a&gt; *video also available in the GOTO Play app)&lt;/p&gt;

&lt;h2&gt;
  
  
  Sonic Pi
&lt;/h2&gt;

&lt;p&gt;Now this, for me, was one of the coolest moments from the conference.&lt;/p&gt;

&lt;p&gt;Have you ever wondered what would happen if you would take programmer and a DJ and not only combine them into one, but also have the put on a show. Well, neither did I, that’s why this presentation ( show / set you can call if whatever you like) blew me away, because from the schedule description I didn’t really know what to make of it so I went in there with low expectations and boy was I in for a surprise.&lt;/p&gt;

&lt;p&gt;This guy, Sam Aaron, built this tool, &lt;a href="https://sonic-pi.net/"&gt;Sonic Pi&lt;/a&gt;, which is basically an IDE for writing music trough code. This started from the desire to try and introduce more entertaining ways for kids and adults to get into coding and evolved in like a full fledged instrument for a DJs to drop some serious beats.&lt;/p&gt;

&lt;p&gt;Oh yeah, Sam not only built this piece of software, he is also playing it like a total boss.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IWcgPEkZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vj1to5ig0qeyw6f5nb5o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IWcgPEkZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vj1to5ig0qeyw6f5nb5o.jpg" alt="GOTO Berlin party"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;He turned the whole conference into a club with live music. There was something for everybody, great music (and I mean it, you wouldn’t guess it live programmed), live programming with proper indenting and naming and also comments, that’s just how programmers communicate with their code’s audience.&lt;/p&gt;

&lt;p&gt;You can imagine that the first thing I did after my hangover was to download Sonic Pi and try it out. I don’t have any musical talent, but there are some Soundcloud songs which are described very technically, that you can use to make you very own Hello Worlds debut into the music industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend Architecture
&lt;/h2&gt;

&lt;p&gt;Of course there was Frontend at this conference as well. It wasn’t only idealistic discussions and entertainment. &lt;/p&gt;

&lt;p&gt;It kind of was the same for this discussion as well, I mean, frontend architecture kind of falls into the general direction of idealistic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZzU4fA6p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rd1lwnaq6c0cm0diggw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZzU4fA6p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rd1lwnaq6c0cm0diggw7.png" alt="Architecture has become a dirty word"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But it’s not only frontend, we don’t want to single out anyone. Architecture in general is associated with a certain unconformable gut feeling that you cannot really put your finger on it. &lt;/p&gt;

&lt;p&gt;So Monica tried to break down this “Architecture” stuff and see what really is  that makes it so shady. And stuff started coming out, Technical Debt, Dependency Injection, Component Reusability. Scary stuff right there, lucky she is a great and funny speaker, so people didn’t just left the room instantly when they’ve seen yet another person going over same stuff everybody keeps lecturing about. And she also made some bold, but otherwise good points. &lt;/p&gt;

&lt;p&gt;It seems an aspect of good architecture is resilience to changes over time. So yeah, the graph bellow might explain why.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u6saPa5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pjvet7xsez8zn64v3s6h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u6saPa5H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pjvet7xsez8zn64v3s6h.jpg" alt="Recurring technical debt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To achieve this here are some things we can consider during development.&lt;/p&gt;

&lt;p&gt;Let’s start with an easy one. All source code dependencies must point inwards, so from the most outer layer, like Router to the most inner one, like a model or service. That way you avoid a huge intertwined ball of mud. I think that’s a reasonable aspect. Let’s do another one.&lt;/p&gt;

&lt;p&gt;I realize while typing that this contradicts every fiber in me, but, we don’t always need to generalize every component which we realize can be used in another place (oh, that hurt). It turns out sometimes you can even copy code and avoid the configurability hell you get in when you encounter small differences between two or more places a component is used in.&lt;/p&gt;

&lt;p&gt;I won’t go trough the whole presentation stuff here, because, as you already know you can check it out online.&lt;/p&gt;

&lt;p&gt;(&lt;a href="https://gotober.com/2019/sessions/1119/building-resilient-frontend-architecture"&gt;https://gotober.com/2019/sessions/1119/building-resilient-frontend-architecture&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As Berlin is, GOTO is also a colorful, diverse, green and progressive conference, situated in one of the most important tech hubs in Germany. I took away with me some important learnings, contacts and experience from it and I can’t wait to what future editions will bring.&lt;/p&gt;

&lt;p&gt;As I said in the beginning I want to help you decide if you should join GOTO in Berlin, so I’ll leave you with a personal rating for the conference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interesting: 3/5 👍&lt;/li&gt;
&lt;li&gt;Usefulness or applicability from: 3/5 👍&lt;/li&gt;
&lt;li&gt;Fun factor: 4/5 👍&lt;/li&gt;
&lt;li&gt;Schmooz factor: 3/5 👍&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>conference</category>
      <category>gotober</category>
      <category>motivation</category>
      <category>techtalks</category>
    </item>
  </channel>
</rss>
