<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Dev Notes</title>
	<atom:link href="https://davidtruxall.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://davidtruxall.com</link>
	<description>David Truxall</description>
	<lastBuildDate>Fri, 20 Dec 2024 15:39:34 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>

<image>
	<url>https://davidtruxall.com/wp-content/uploads/2016/09/cropped-mobilenote-1-32x32.png</url>
	<title>Dev Notes</title>
	<link>https://davidtruxall.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Separate GraphiQL from DGS</title>
		<link>https://davidtruxall.com/separate-graphiql-from-dgs/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Fri, 20 Dec 2024 15:39:34 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[DGS]]></category>
		<category><![CDATA[graphiql]]></category>
		<category><![CDATA[graphql]]></category>
		<category><![CDATA[java]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=876</guid>

					<description><![CDATA[DGS (Domain Graph System) is a GraphQL implementation created by Netflix for the JVM. DGS comes bundled with a GraphiQL implementation, which is very convenient.&#8230;]]></description>
										<content:encoded><![CDATA[
<p><a href="https://netflix.github.io/dgs/">DGS (Domain Graph System) </a>is a GraphQL implementation created by Netflix for the JVM. DGS comes bundled with a GraphiQL implementation, which is very convenient. But, there might be reasons to separate the implementation, for instance modifying it (look and feel or adding a plugin), or even for a bug fix, which is my problem. In the region my customer works, the <a href="https://unpkg.com/">UNPKG</a> CDN has not been serving up the React core library for over two weeks now, so anyone who didn&#8217;t already have GraphiQL in their browser cache can&#8217;t load the site and use the tool. This post describes how to get your own implementation working in DGS.</p>



<p>The first step is to capture the HTML from your working DGS implementation. Visit the GraphiQL page, and grab it&#8217;s source. Save that as <code>graphiql.html</code> in the <code>src/main/resources/static</code> directory in your project.</p>



<p>Now that we have the HTML, we need to stop DGS from serving the embedded version of GraphiQL. In your <code>application.properties</code> file add this line:</p>



<pre class="wp-block-preformatted">dgs.graphql.graphiql.enabled=false<br></pre>



<p>The last step is to create an endpoint in Spring Boot to serve the HTML. Create a Spring Boot <code>@Configuration</code> Java class:</p>



<pre class="wp-block-preformatted">import org.springframework.context.annotation.Configuration;<br>import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;<br>import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;<br><br>@Configuration(proxyBeanMethods = false)<br>public class GraphiQlConfig implements WebMvcConfigurer {<br><br>    @Override<br>    public void addViewControllers(ViewControllerRegistry registry) {<br>        registry.addViewController("/graphiql").setViewName("graphiql.html");<br>    }<br>}</pre>



<p>Now, restarting the application causes <code>&lt;app-url>/graphiql</code> to serve up the HTML. You are free to modify GraphiQL in whatever manner you see fit.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Parameter Position in OpenAPI</title>
		<link>https://davidtruxall.com/parameter-position-in-openapi/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Wed, 27 Nov 2024 00:54:00 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Strange Problems]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[openAPI]]></category>
		<category><![CDATA[quarkus]]></category>
		<category><![CDATA[usability]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=854</guid>

					<description><![CDATA[OpenAPI provides a terrific UI to use for testing, learning and using your APIs. My team has built some APIs using Quarkus, and used their&#8230;]]></description>
										<content:encoded><![CDATA[
<p>OpenAPI provides a terrific UI to use for testing, learning and using your APIs. My team has built some APIs using Quarkus, and used <a href="https://quarkus.io/guides/openapi-swaggerui">their implementation of the SmallRye OpenAPI library.</a> The problem I&#8217;ve run into is that the parameters of any given API call come out in alphabetical order, not the order specified in the code.  Here&#8217;s a sample of the code with some OpenAPI metadata:</p>



<pre class="wp-block-preformatted">@GET<br>@Path("/range")<br>@Operation(summary = "Get a list of resources inclusive for a range of dates")<br>public RangeResult getRecordsByRange(<br>        @Context UriInfo urlInfo,<br>        @Parameter(description = "The starting date", required = true, example = "yyyy-MM-dd")<br>        @QueryParam("startDate") String startDate,<br>        @Parameter(description = "The ending date, inclusive of records generated on that day", required = true, example = "yyyy-MM-dd")<br>        @QueryParam("endDate") String endDate,<br>        @Parameter(description = "The current data page, using a zero-based index. Default value is zero (the first page)", required = false, example = "2")<br>        @QueryParam("page") int page,<br>        @Parameter(description = "The number of records per page (max 1000). Default value is 100.", required = false, example = "100")<br>        @QueryParam("pageSize") int pageSize) throws ApiException {<br>}</pre>



<p>Unfortunately, that code results in this:</p>



<figure class="wp-block-image size-large is-style-default"><img fetchpriority="high" decoding="async" width="1024" height="393" src="https://davidtruxall.com/wp-content/uploads/2024/04/incorrect-order-1024x393.png" alt="Parameters are in a seemingly random order compared to the code. Seems to be alphabetical." class="wp-image-855" srcset="https://davidtruxall.com/wp-content/uploads/2024/04/incorrect-order-1024x393.png 1024w, https://davidtruxall.com/wp-content/uploads/2024/04/incorrect-order-300x115.png 300w, https://davidtruxall.com/wp-content/uploads/2024/04/incorrect-order-768x295.png 768w, https://davidtruxall.com/wp-content/uploads/2024/04/incorrect-order-1536x590.png 1536w, https://davidtruxall.com/wp-content/uploads/2024/04/incorrect-order-2048x786.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>This doesn&#8217;t make sense to me for multiple reasons. First, the <em>endDay</em> parameter is <strong>before</strong> the <em>startDay</em> parameter. Second, those same parameters are required and they occur first and last. To make this more intuitive for the user, I&#8217;d want to group the required parameters at the beginning, and ensure that <em>startDay</em> comes before <em>endDay</em>. </p>



<p>The solution was suggested in a <a href="https://github.com/quarkusio/quarkus/issues/38736">Github issue</a> for Quarkus, I did not find the solution elsewhere, and it certainly was not intuitive. The answer was to not place the <code>@Parameter</code> metadata inline with the function parameters (like all the OpenAPI examples I could find). By separating them from the function definition and placing them above the function, OpenAPI respected the order. I changed the code to this:</p>



<pre class="wp-block-preformatted">@GET
@Path("/range")
@Operation(summary = "Get a list of resources inclusive for a range of dates")
@Parameter(description = "The starting date", required = true, example = "yyyy-MM-dd")
@Parameter(description = "The ending date, inclusive of records generated on that day", required = true, example = "yyyy-MM-dd")
@Parameter(description = "The current data page, using a zero-based index. Default value is zero (the first page)", required = false, example = "2")
@Parameter(description = "The number of records per page (max 1000). Default value is 100.", required = false, example = "100")
public RangeResult getRecordsByRange(
        @Context UriInfo urlInfo,
        @QueryParam("startDate") String startDate,
        @QueryParam("endDate") String endDate,
        @QueryParam("page") int page,        
        @QueryParam("pageSize") int pageSize) throws ApiException {
}</pre>



<p>That change resulted in the below image, which is exactly what I wanted:</p>



<figure class="wp-block-image size-large is-style-default"><img decoding="async" width="1024" height="428" src="https://davidtruxall.com/wp-content/uploads/2024/04/correct-order-1024x428.png" alt="Parameters are in the order I want. The required parameters are at the top of the list. The startDay parameter comes before the endDay parameter." class="wp-image-857" srcset="https://davidtruxall.com/wp-content/uploads/2024/04/correct-order-1024x428.png 1024w, https://davidtruxall.com/wp-content/uploads/2024/04/correct-order-300x125.png 300w, https://davidtruxall.com/wp-content/uploads/2024/04/correct-order-768x321.png 768w, https://davidtruxall.com/wp-content/uploads/2024/04/correct-order-1536x642.png 1536w, https://davidtruxall.com/wp-content/uploads/2024/04/correct-order-2048x857.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Exposing Kafka in Docker Desktop Kubernetes</title>
		<link>https://davidtruxall.com/exposing-kafka-in-docker-desktop-kubernetes/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Wed, 03 Apr 2024 00:06:00 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Tools]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=840</guid>

					<description><![CDATA[In an effort to get more fluent with Kubernetes, I&#8217;m using it instead of Docker/Docker compose during my local development. I&#8217;m using Docker Desktop on&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In an effort to get more fluent with Kubernetes, I&#8217;m using it instead of Docker/Docker compose during my local development. I&#8217;m using Docker Desktop on a Mac, so it&#8217;s going to be slightly different than minikube or on Windows.</p>



<p>In most cases I can take a Docker Compose file and recreate it as a services and deployments that my code can interact with. But in the case of Kafka, there are a couple complexities that I needed to iron out to get it running. The two main problems were exposing the Kafka UI web app from inside the cluster, and exposing Kafka in a way that both Kafka UI and applications running outside the cluster (maybe from an IDE or command-line) can access topics.</p>



<p>The first problem is to expose Kafka both inside and outside the cluster. Inside the cluster it gets used by Kafka UI, and outside the cluster it gets used by the code I&#8217;m creating. The trick is in both the Service and Deployment definitions. In the Service definition, I set the type to NodePort, but I define ports for both internal and external access, with only the external access using a nodeport:</p>



<pre id="block-cedb886e-dc1e-4bef-8fd5-15c35349d1b8" class="wp-block-preformatted">apiVersion: v1<br>kind: Service<br>metadata:<br>  labels:<br>    app: kafka-broker<br>  name: kafka-service<br>  namespace: dev<br>spec:<br>  type: NodePort<br>  ports:<br>  - port: 9092<br>    name: inner<br>    targetPort: 9092<br>    protocol: TCP<br>  - port: 30092<br>    name: outer<br>    targetPort: 30092<br>    nodePort: 30092<br>    protocol: TCP<br>  selector:<br>    app: kafka-broker</pre>



<p>Setting up the broker is more complex. First, we make an alias for PLAINTEXT:</p>



<p><pre><code>- name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP           <br>  value: "INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT"</code></pre></p>



<p>Then, make sure the container is exposed on two ports:</p>



<p><pre><code>ports:         </code><br><code>- containerPort: 9092         </code><br><code>- containerPort: 30092</code></pre></p>



<p>Port 9092 is the standard Kafka port, and is used inside the cluster. Port 30092 is in the accepted range for nodeports, and is the port used outside the cluster. Now, we tell Kafka to listen on those ports using the aliases, and advertise listeners both inside the cluster and using the domain we initially defined:</p>



<p><pre>- name: KAFKA_INTER_BROKER_LISTENER_NAME<br>  value: "INSIDE"<br>- name: KAFKA_LISTENERS<br>  value: "INSIDE://:9092,OUTSIDE://:30092"<br>- name: KAFKA_ADVERTISED_LISTENERS<br>  value: "INSIDE://$(KAFKA_SERVICE_SERVICE_HOST):9092,OUTSIDE://kafka.example.net:30092"</pre></p>



<p>Here&#8217;s the full Deployment:</p>



<pre id="block-cedb886e-dc1e-4bef-8fd5-15c35349d1b8" class="wp-block-preformatted">apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: kafka-broker
  name: kafka-broker
  namespace: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kafka-broker
  template:
    metadata:
      labels:
        app: kafka-broker
    spec:
      hostname: kafka-broker
      containers:
      - env:
        - name: KAFKA_BROKER_ID
          value: "1"
        - name: KAFKA_ZOOKEEPER_CONNECT
          value: $(ZOOKEEPER_SERVICE_SERVICE_HOST):2181
        - name: KAFKA_INTER_BROKER_LISTENER_NAME
          value: "INSIDE"
        - name: KAFKA_LISTENERS
          value: "INSIDE://:9092,OUTSIDE://:30092"
        - name: KAFKA_ADVERTISED_LISTENERS
          value: "INSIDE://$(KAFKA_SERVICE_SERVICE_HOST):9092,OUTSIDE://kafka.example.net:30092"
        - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP
          value: "INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT"
        - name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR
          value: "1"
        - name: KAFKA_TRANSACTION_STATE_LOG_MIN_ISR
          value: "1"
        - name: KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR
          value: "1"
        image: confluentinc/cp-kafka:7.6.0
        imagePullPolicy: IfNotPresent
        name: kafka-broker
        ports:
        - containerPort: 9092
        - containerPort: 30092</pre>



<p>The next problem, exposing Kafka UI, is not unique to Kafka, but is a problem for any web app you run inside the Docker Desktop cluster. I didn&#8217;t want to use port forwarding. While I know that works, it&#8217;s not as convenient as just accessing a URL.</p>



<p>The first thing I set up was URL resolution in my <code>hosts</code> file. I edited my <code>/etc/hosts</code> file and added the following line:</p>



<p><code>127.0.0.1 kafka.example.net</code></p>



<p>I have taken to using<code> example.net</code> for my cluster resources, and <code>example.com</code> for my Docker Compose resources. <a href="https://www.iana.org/help/example-domains">Both those domains are reserved</a> and won&#8217;t conflict with actual domains.</p>



<p>The next step is to install an <a href="https://kubernetes.github.io/ingress-nginx/deploy/#docker-desktop">nginx ingress into my local cluster</a>:</p>



<pre class="wp-block-preformatted"><code>kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.0/deploy/static/provider/cloud/deploy.yaml</code></pre>



<p>Now I can set up Kafka UI:</p>



<pre id="block-cedb886e-dc1e-4bef-8fd5-15c35349d1b8" class="wp-block-preformatted">apiVersion: v1
kind: Service
metadata:
  labels:
    app: kafka-ui-service
  name: kafka-ui-service
  namespace: dev
spec:
  ports:
    - name: kafka-ui-port
      port: 80
      targetPort: 8080
  selector:
    app: kafka-ui
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: kafka-ui
  name: kafka-ui
  namespace: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kafka-ui
  template:
    metadata:
      labels:
        app: kafka-ui
    spec:
      hostname: kafka-ui
      containers:
      - env:
        - name: KAFKA_CLUSTERS_0_NAME
          value: "local"
        - name: KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS
          value: $(KAFKA_SERVICE_SERVICE_HOST):9092
        image: provectuslabs/kafka-ui:master
        imagePullPolicy: IfNotPresent
        name: kafka-ui
        ports:
        - containerPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kafka-ui-ingress
  namespace: dev
spec:
  ingressClassName: nginx
  rules:
  - host: kafka.example.net
    http:
      paths:
      - backend:
          service:
            name: kafka-ui-service
            port:
              number: 80
        path: /
        pathType: Prefix</pre>



<p>I&#8217;ve placed the<a href="https://github.com/davetrux/local-systems/blob/main/kubernetes/kafka.yaml"> complete yaml file in a repo</a>, and <a href="https://stackoverflow.com/questions/50092446/unable-to-query-kafka-in-kubernetes-deployment">this post</a> sent me in the right direction for my solution.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>GitHub Setup on a New Mac</title>
		<link>https://davidtruxall.com/git-setup-on-a-new-mac/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Tue, 23 Aug 2022 22:00:00 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[GitHub]]></category>
		<category><![CDATA[gpg]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[pgp]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=783</guid>

					<description><![CDATA[I recently acquired a new Mac, and needed to set up git with access to Github on the new computer. I decided to document the&#8230;]]></description>
										<content:encoded><![CDATA[
<p>I recently acquired a new Mac, and needed to set up git with access to Github on the new computer. I decided to document the process.</p>



<p>First, create a new ssh key, adding a password:</p>



<pre id="block-1ff7969a-5d5d-49b3-a81e-80431aaaeca0" class="wp-block-code"><code>ssh-keygen -t rsa -b 4096</code></pre>



<p>This key ends up in the <code>.ssh</code> folder of your home directory: <code>~/.ssh/rsa_id</code></p>



<p>Now, copy the public key to the clipboard to add it to GitHub:</p>



<pre class="wp-block-preformatted"><code>cat ~/.ssh/id_rsa.pub | pbcopy</code></pre>



<p>In GitHub, navigate to the account settings, and choose SSH and GPG keys from the left navigation menu. Click the New SSH Key button. Type a Title for the key, and paste the clipboard contents into the Key field. Click the Add SSH Key button to save it.</p>



<p>Configure git locally, using the email address from the GitHub account:</p>



<pre id="block-1ff7969a-5d5d-49b3-a81e-80431aaaeca0" class="wp-block-code"><code>git config --global user.email "&lt;YourEmail@somewhere.com&gt;"
git config --global user.name "&lt;YourUserName&gt;"</code></pre>



<p>Instead of typing a password for every use of git, store the password in the keychain locally. In the <code>.ssh </code>folder, create a file named <code>config</code>:</p>



<pre class="wp-block-preformatted">touch ~/.ssh/config</pre>



<p>Open the <code>config</code> file in an editor, and add the following content:</p>



<pre id="block-1ff7969a-5d5d-49b3-a81e-80431aaaeca0" class="wp-block-code"><code>Host *<br>    UseKeychain yes<br>    AddKeysToAgent yes</code></pre>



<p>The next push to GitHub will prompt for the password, and from that point on it&#8217;s stored in keychain and git won&#8217;t prompt for the password any more.</p>



<p>Since I am using GitHub, I use a GPG key to verify my identity when I commit and push code.  I can see my commits are verified, protecting me against someone <a href="https://www.securityweek.com/supply-chain-attack-technique-spoofs-github-commit-metadata" target="_blank" rel="noreferrer noopener">spoofing pushes to my repos</a>:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="259" src="https://davidtruxall.com/wp-content/uploads/2022/08/verified-1024x259.png" alt="Verified commits on GitHub" class="wp-image-802" srcset="https://davidtruxall.com/wp-content/uploads/2022/08/verified-1024x259.png 1024w, https://davidtruxall.com/wp-content/uploads/2022/08/verified-300x76.png 300w, https://davidtruxall.com/wp-content/uploads/2022/08/verified-768x194.png 768w, https://davidtruxall.com/wp-content/uploads/2022/08/verified.png 1518w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The first step is creating a GPG key. I installed and used <a href="https://gpgtools.org/">Mac GPG Tools</a> to create my key. After installation the program launches. Create a new key using a strong password and the same email address used for GitHub:</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://davidtruxall.com/wp-content/uploads/2022/08/gpg-key-1024x582.png" alt="Creating a GPG key using the Mac client" class="wp-image-803" width="768" height="437" srcset="https://davidtruxall.com/wp-content/uploads/2022/08/gpg-key-1024x582.png 1024w, https://davidtruxall.com/wp-content/uploads/2022/08/gpg-key-300x171.png 300w, https://davidtruxall.com/wp-content/uploads/2022/08/gpg-key-768x437.png 768w, https://davidtruxall.com/wp-content/uploads/2022/08/gpg-key-1536x873.png 1536w, https://davidtruxall.com/wp-content/uploads/2022/08/gpg-key-2048x1164.png 2048w" sizes="(max-width: 768px) 100vw, 768px" /></figure>



<p>Choose a strong passphrase:</p>



<figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://davidtruxall.com/wp-content/uploads/2022/08/gpg-passphrase-1024x512.png" alt="Creating the passphrase for the GPG key" class="wp-image-804" width="512" height="256" srcset="https://davidtruxall.com/wp-content/uploads/2022/08/gpg-passphrase-1024x512.png 1024w, https://davidtruxall.com/wp-content/uploads/2022/08/gpg-passphrase-300x150.png 300w, https://davidtruxall.com/wp-content/uploads/2022/08/gpg-passphrase-768x384.png 768w, https://davidtruxall.com/wp-content/uploads/2022/08/gpg-passphrase.png 1132w" sizes="(max-width: 512px) 100vw, 512px" /></figure>



<p>After creating a passphrase for the key, install the public key at GitHub. First, copy the public key to a file using the email address used while creating the key, then copy the file contents to the clipboard:</p>



<pre class="wp-block-preformatted">gpg --export --armor YourEmail@somewhere.com &gt; public-key.asc
cat public-key.asc | pbcopy</pre>



<p>In GitHub, navigate to account settings, and choose SSH and GPG keys from the left navigation menu. Click the New GPG Key button. Type a Title for the key, and paste the clipboard contents into the Key field. Click the Add GPG Key button to save it.</p>



<p>Next, configure git to use the GPG key. First, find the ID for the key:</p>



<pre class="wp-block-preformatted">gpg --list-secret-keys --keyid-format=long</pre>



<p>The output should look like this:</p>



<pre class="wp-block-preformatted">/Users/username/.gnupg/pubring.kbx

sec rsa4096/EAF3888888888888E 2022-07-17 [SC] [expires: 2026-07-17]
919488888888888888888888888888888888888E
uid [ultimate] Your Name YourEmail@somewhere.com
ssb rsa4096/A888888888888884 2022-07-17 [E] [expires: 2026-07-17]</pre>



<p>On the line below the one with your email address, copy the text after the <code>rsa4096/</code> and before the date generated. Use that ID to configure git to sign commits:</p>



<pre id="block-1ff7969a-5d5d-49b3-a81e-80431aaaeca0" class="wp-block-code"><code>git config --global user.signingkey A888888888888884</code></pre>



<p>Now, when ready commit some code destined for GitHub, add a new parameter to the command: <code>-S</code></p>



<pre id="block-1ff7969a-5d5d-49b3-a81e-80431aaaeca0" class="wp-block-code"><code>git commit -S -m "A clear commit message"</code></pre>



<p>The first time committing with the new parameter, Mac GPG prompts for the passphrase set on the key above. You can choose to save the passphrase in the keychain for future commits.</p>



<p>Now my machine is set up to work with GitHub and verify my identity on my commits.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Deploy A Vue App in Docker Without Root</title>
		<link>https://davidtruxall.com/deploy-a-vue-app-in-docker-without-root/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Wed, 17 Aug 2022 00:44:26 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Tools]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[ssl]]></category>
		<category><![CDATA[tls]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=759</guid>

					<description><![CDATA[So you&#8217;re going to deploy your Vue app in a Docker container. That great!! Containers are a fantastic way to deploy your app. When I&#8230;]]></description>
										<content:encoded><![CDATA[
<p>So you&#8217;re going to deploy your Vue app in a Docker container. That great!! Containers are a fantastic way to deploy your app. When I deploy Vue apps, I choose <a rel="noreferrer noopener" href="https://nginx.org/en/" target="_blank">nginx</a> as the web server. nginx is available as a Docker image from <a rel="noreferrer noopener" href="https://hub.docker.com/_/nginx" target="_blank">Dockerhub</a>, so you don&#8217;t need to do much to get started. Unfortunately the default implementation runs in the context of the root user. This can be a security problem, especially if the container gets breached. The attacker is now running as root.</p>



<p>Unfortunately, it&#8217;s not quite as simple as just changing the user in the Dockerfile. The reason the nginx image runs as root is that in Linux, the user must be root in order to run the app on port 80 or 443. We can make the changes to the container to make this possible, but the changes are complex. Luckily we are using a container, so the actual port the web server runs on in the container is just not relevant. So we can run the app in the context of a non-root user on any other port (like 8080 for instance). When running the container, we can map back to port 80 or 443 for production deployments if we need to expose the app directly to the Internet. In my case, the SSL/TLS certificate is hosted either in a reverse proxy or a Kubernetes ingress, so I am not including the certificate in my Docker images.</p>



<p>The first thing we need to change is the main configuration file for nginx. We want it to listen on another port, this time it&#8217;s going to be 8080. The rest of the configuration is a default setting:, but it could be there if we are exposing the app directly on port 443:</p>



<pre class="wp-block-preformatted">server {<br>  listen       8080;<br>  server_name  localhost;<br><br>  location / {<br>    root   /usr/share/nginx/html;<br>    index  index.html index.htm;<br>    try_files $uri $uri/ /index.html;<br>  }<br><br>  error_page   400 500 502 503 504  /50x.html;<br>  location = /50x.html {<br>    root   /usr/share/nginx/html;<br>  }<br>}</pre>



<p>Next, we need to change the user context nginx runs under. Luckily, the nginx folks have thought about this, and already created a user called <em>nginx</em> right in the default container, so there&#8217;s no system-level user configuration necessary. Here&#8217;s the complete Dockerfile:</p>



<pre class="wp-block-preformatted">FROM nginx:1.19

RUN rm -f /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d

RUN chown -R nginx:nginx /var/cache/nginx &amp;&amp; \
    chown -R nginx:nginx /var/log/nginx &amp;&amp; \
    chown -R nginx:nginx /etc/nginx/conf.d

RUN touch /var/run/nginx.pid &amp;&amp; \
    chown -R nginx:nginx /var/run/nginx.pid

USER nginx

COPY dist /usr/share/nginx/html

EXPOSE 8080</pre>



<p>Let&#8217;s look at the relevant parts of the Dockerfile. There are a few directories where the nginx user must have ownership rights for logging, caching and configuration, as well as the process ID file:</p>



<pre id="block-c04761d6-cd25-4ef9-801b-29bcf4a8033c" class="wp-block-code"><code>RUN chown -R nginx:nginx /var/cache/nginx &amp;&amp; \
    chown -R nginx:nginx /var/log/nginx &amp;&amp; \
    chown -R nginx:nginx /etc/nginx/conf.d

RUN touch /var/run/nginx.pid &amp;&amp; \
    chown -R nginx:nginx /var/run/nginx.pid</code></pre>



<p>We set the user context next, so nginx runs under this user:</p>



<pre id="block-c04761d6-cd25-4ef9-801b-29bcf4a8033c" class="wp-block-code"><code>USER nginx</code></pre>



<p>Then the Dockerfile copies the contents of the <code>dist</code> folder into the image. This is the output from building our Vue app with npm:</p>



<p><code>COPY dist /usr/share/nginx/html </code></p>



<p>And lastly we set the port, which can&#8217;t be 80 or 443:</p>



<pre id="block-c04761d6-cd25-4ef9-801b-29bcf4a8033c" class="wp-block-code"><code>EXPOSE 8080</code></pre>



<p>Now our Dockerfile is set to create a container that is not running with root privileges. The app can be run over 80 or 443 using Docker, a Kubernetes ingress, or even a reverse proxy, with a smaller amount of risk than using the defaults.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Breaking Production</title>
		<link>https://davidtruxall.com/breaking-production/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Thu, 24 Jun 2021 15:00:03 +0000</pubDate>
				<category><![CDATA[Career]]></category>
		<category><![CDATA[Novell]]></category>
		<category><![CDATA[Strange Problems]]></category>
		<category><![CDATA[break]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[hbomax]]></category>
		<category><![CDATA[lesson]]></category>
		<category><![CDATA[production]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=754</guid>

					<description><![CDATA[Recently, an intern at HBO Max mistakenly sent a test email to thousands of users in production. Twitter was on fire with memes and jokes,&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Recently, an intern at HBO Max <a href="https://www.newsweek.com/hbo-max-sparks-jokes-memes-integration-test-email-1601837">mistakenly sent a test email to thousands of users in production</a>. Twitter was on fire with memes and jokes, but I found the threads about developers&#8217; experiences breaking production to be much more interesting. Most long-time developers experience the gut-wrenching experience of breaking production, and I&#8217;m no different. My story is more complicated than I could get in a Tweet, so I&#8217;m telling the story here.</p>



<p>The year was 2003. I was working at a company in the Detroit area migrating a classic ASP application to ASP.Net. The site &#8220;just had constant errors&#8221; that no one could elaborate on, and needed a thorough overhaul. The company decided to use the need to rewrite code as an opportunity to move to .Net since Microsoft was moving way from Classic ASP at that time. Unfortunately for me, there was no logging in the existing system, and all the exceptions were unhandled so my team had no idea what was breaking or when it was happening. To try and get a grip on the problem, we wrote code to hook into the global error handler and have the server send everyone on the team an email with the error details so we could start to understand the problems and frequency by getting real-time alerts. There were no Sentry.io/Crashlytics/LogRocket services or the like at that time, so we built our own. We were testing this feature, and had not rolled it out to production yet. It was only in our development environment. </p>



<p>The day was August 14, 2003. That afternoon, there was a <a href="https://en.wikipedia.org/wiki/Northeast_blackout_of_2003">severe blackout across the northeastern US</a>. When it was clear the power was not coming back on, the company sent us home. For us the blackout lasted days. And this is where my failure begins.</p>



<p>The system I was working on was backed by an Oracle database, which had a dedicated administrator. The web servers had a different person administering them. During the blackout, the data center, which was located at our facility in Detroit, was running off of generators to keep services available to users outside the blackout area. The database administrator decided to shut down the development database server to conserve energy. But the web server admin kept the development web server running. Unbeknownst to me, the web server admin was running a tool that pinged the home page of the development web site every 8 seconds to make sure it was still alive. Unfortunately, the home page of the web site accessed the database, which was turned off. This caused an unhandled exception on the home page. We already know the site was not handling any exceptions. So the error fired the global exception handler and sent me and my team an email about the error. Every 8 seconds. For two days, because no one on my team was working during the blackout to see the emails.</p>



<p>We returned to the office when power was restored. No one in the company could get email though. That system was still down. Turns out it was down because of my code. The handler sent 56,000 emails during the blackout and filled the disk of the <a href="https://en.wikipedia.org/wiki/GroupWise">Novell GroupWise</a> email server. Back in 2003 disks were no where near the size we use today. The email administrator was furious. No administrative tool existed for her to remove all those emails. My team had to sit for hours and hours and hours deleting emails using the GroupWise desktop client, which was limited to only selecting 100 messages at a time. We certainly did our penance.</p>



<p>You might be thinking that the circumstances of the blackout caused the problem, not really a mistake in the code.  But the fault was mine. I should have considered throttling in the error handler. It should have stopped sending the same error message repeatedly. There is no value seeing the same error over and over and over, especially over a short period of time. This was of course the first thing I fixed after deleting all the emails. It&#8217;s a hard-earned lesson I&#8217;ll never forget.</p>



<p>I may have broken production at other times, but nothing as dramatic and difficult to recover from as this. If you are a junior person reading this, I can assure you that some day your code will break something. But you are not alone, it happens to all of us. I hope your breaks are minor and less consequential, but I know what you are feeling. Feel it and then take that lesson to heart and you&#8217;ll become a better developer.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Keycloak for Local Development</title>
		<link>https://davidtruxall.com/keycloak-for-local-development/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Tue, 30 Mar 2021 10:00:00 +0000</pubDate>
				<category><![CDATA[Security]]></category>
		<category><![CDATA[Keycloak]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=720</guid>

					<description><![CDATA[Keyloak is an open source identity management tool. If you are learning how to make your app use authentication, it&#8217;s an ideal tool if you&#8230;]]></description>
										<content:encoded><![CDATA[
<p><a rel="noreferrer noopener" href="https://www.keycloak.org/" target="_blank">Keyloak</a> is an open source identity management tool. If you are learning how to make your app use authentication, it&#8217;s an ideal tool if you don&#8217;t have any other source. It&#8217;s straightforward to learn and setup, yet hast pretty sophisticated features to grow into. For this article, we are going to set up Keycloak to run locally using Docker. </p>



<h2 class="wp-block-heading">TLDR; The Steps</h2>



<p>Assuming you have Docker installed locally, these are the general steps to set Keycloak up for local use:</p>



<ol class="wp-block-list"><li>Pull and launch the Docker image</li><li>Set up a realm</li><li>Create a client (app registration)</li><li>Create a role for the users of the client</li><li>Create users</li><li>Assign users to the role</li></ol>



<h3 class="wp-block-heading">Docker</h3>



<p>We will run the Docker container locally.  The admin account gets created when creating the container. For this demonstration I&#8217;m just going to use <code>admin/admin</code> for the username and password. Don&#8217;t do that if you install Keycloak outside of your box, create a strong password. Run this Docker command on the command-line to launch Keycloak:</p>



<p><code>docker run --name keycloak-dev -d -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -p 8001:8080 quay.io/keycloak/keycloak:18.0.1</code></p>



<p>For this sample I&#8217;m using version 12.0.4, which will probably be out-of-date when you try this. So things can change, be aware these steps may no longer match in the future. The admin user gets created by the environment variables. The <code>-d</code> flag runs the container in the background and frees up your command line. Keycloak will start up using an embedded H2 database. If you destroy the container you&#8217;ll lose all your configuration. In a real deployment I&#8217;d use a container running PostGREs to store the data. For local development the embedded database is fine and much quicker to set up.</p>



<p>Keycloak will be running on port 8001 at http://localhost:8001. You&#8217;ll have to wait a bit for Keycloak to spin up. You can always check the log using:</p>



<p><code>docker logs keycloak-dev</code></p>



<p>When you see a message similar to &#8220;Keycloak 12.0.4 (WildFly Core 13.0.3.Final) started in 18938ms&#8221; you know it&#8217;s ready.</p>



<h3 class="wp-block-heading">Set Up a Realm</h3>



<p>Open <a rel="noreferrer noopener" href="http://localhost:8001" target="_blank">http://localhost:8001</a> in your browser. You should see the staring screen.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="933" height="649" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-start.png" alt="Keycloak starting web page" class="wp-image-723" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-start.png 933w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-start-300x209.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-start-768x534.png 768w" sizes="(max-width: 933px) 100vw, 933px" /><figcaption>Figure 1: Keycloak starting web page</figcaption></figure>



<p>Click the Administration Console link. Log in with the credentials used to create the Docker image above. Let&#8217;s create a Realm, which is a container for the apps you want to protect behind Keycloak. At first login you will be in the master realm, and you should not use that for your apps.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="809" height="716" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-realm.png" alt="Add a new realm to Keycloak" class="wp-image-724" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-realm.png 809w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-realm-300x266.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-realm-768x680.png 768w" sizes="(max-width: 809px) 100vw, 809px" /><figcaption>Figure 2: Add a new realm to Keycloak</figcaption></figure>



<p>Click the drop-down arrow next to the Master realm name in the left navigation pane, then click the Add Realm button. Give the realm a name and remember it for later, you&#8217;ll need it when configuring an app to authenticate using Keycloak. </p>



<h3 class="wp-block-heading">Create A Client</h3>



<p>Click the Clients link in the left navigation pane.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="434" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client-1024x434.png" alt="Add a new client to Keycloak" class="wp-image-726" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client-1024x434.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client-300x127.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client-768x326.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client.png 1056w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 3: Add a new client to Keycloak</figcaption></figure>



<p>Click the Create button at the top left of the table of existing Clients to add a new Client for your app. In my case, I&#8217;m going to create a Vue app to demo logging in with Keycloak.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="437" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client-1024x437.png" alt="Name the client and set the root URL" class="wp-image-727" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client-1024x437.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client-300x128.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client-768x327.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client.png 1060w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 4: Name the client and set the root URL</figcaption></figure>



<p>Set the client id as the name of your app, and set the Root URL to the URL you are using for local development of your application. In my case, http://localhost:8080. Click the Save button.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="773" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-client-details-1024x773.png" alt="Set the client details" class="wp-image-733" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-client-details-1024x773.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-client-details-300x226.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-client-details-768x580.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-client-details.png 1259w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 5: Set the client details</figcaption></figure>



<p>In the Client details page, make sure the Client Protocol is set to <code>openid-connect </code>and the Access Type is set to <code>public</code>. Check that the following are set properly:</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td><strong>Property</strong></td><td><strong>Value</strong></td><td><strong>Reason</strong></td></tr><tr><td>Root URL</td><td>http://localhost:8080/</td><td>Gets prepended to redirected URLS</td></tr><tr><td>Valid Redirect URIs</td><td>http://localhost:8080/*</td><td>Redirect location after logout</td></tr><tr><td>Web Origins</td><td>http://localhost:8080</td><td>Allowed origin for <a rel="noreferrer noopener" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank">CORS</a><br>(Really important for web apps)</td></tr></tbody></table></figure>



<p>Save the details and lets add a Role to our Client.</p>



<h3 class="wp-block-heading">Create A Role</h3>



<p>Each Client needs one or more roles. If you don&#8217;t assign roles and check them in your application, any user from your Realm will be able to log into the app. You should create roles to control user access. Click the Roles tab in your Client details page.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="435" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create-1024x435.png" alt="Create a role for the new client" class="wp-image-728" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create-1024x435.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create-300x128.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create-768x327.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create.png 1063w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 6: Create a role for the new client</figcaption></figure>



<p>In the Roles tab, click the Add Role button at the upper right of the table. Give your role a name and save it.</p>



<h3 class="wp-block-heading">Create Users</h3>



<p>We need users to log into our application (Client). We will create a user and then assign a password and role. Click the Users link in the left navigation pane.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="534" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add-1024x534.png" alt="Add a Keycloak user" class="wp-image-729" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add-1024x534.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add-300x156.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add-768x400.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add.png 1055w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 7: Add a Keycloak user</figcaption></figure>



<p>You won&#8217;t see any users listed, even if you added some. Click the Add user button in the top right of the table.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="570" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details-1024x570.png" alt="Add user details" class="wp-image-730" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details-1024x570.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details-300x167.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details-768x427.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details.png 1057w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 8: Add user details</figcaption></figure>



<p>Enter a username, and their actual name and email. Click the Save button. Let&#8217;s give them a password.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="512" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password-1024x512.png" alt="Set the password for the user" class="wp-image-731" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password-1024x512.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password-300x150.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password-768x384.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password.png 1063w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 9: Set the password for the user</figcaption></figure>



<p>On the user details page, click the Credentials tab. Give the user a password. If you don&#8217;t want the user to be forced to change their password, turn the Temporary switch to off. I&#8217;ll do this when I&#8217;m running locally for development, but not when making accounts for actual users in a non-development environment. Click the Set Password button.</p>



<h3 class="wp-block-heading">Assign A Role</h3>



<p>Since the application (Client) has a role, we need to assign that role to the user so they can log into that application. Click the Role Mappings tab.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="569" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role-1024x569.png" alt="Assign a client role to a user" class="wp-image-732" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role-1024x569.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role-300x167.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role-768x427.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role.png 1065w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 10: Assign a client role to a user</figcaption></figure>



<p>Select your client created earlier in the Client Roles drop down list. The role created earlier should be there. Select that role and click the Add selected &gt; button. The user is good to go now.</p>



<p>The next step is to <a href="https://davidtruxall.com/secure-a-vue-js-app-with-keycloak">wire up an application to log in using this local instance of Keycloak</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Secure a Vue.js App with KeyCloak</title>
		<link>https://davidtruxall.com/secure-a-vue-js-app-with-keycloak/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Tue, 30 Mar 2021 10:00:00 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Keycloak]]></category>
		<category><![CDATA[Vue.js]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=718</guid>

					<description><![CDATA[In a recent project I needed to secure a long-standing Vue.js application with Keycloak, a terrific open source identity and access management tool. Unfortunately, the&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In a recent project I needed to secure a long-standing <a rel="noreferrer noopener" href="https://vuejs.org/" target="_blank">Vue.js</a> application with <a rel="noreferrer noopener" href="https://www.keycloak.org/" target="_blank">Keycloak</a>, a terrific open source identity and access management tool. Unfortunately, the Vue <a href="https://www.keycloak.org/securing-apps/vue">sample in Keycloak&#8217;s documentation</a> is flawed. The sample refreshes the token every minute, whether or not the user performs any actions. I don&#8217;t think this behavior is very secure and certainly not going to meet realistic use cases. With this article I&#8217;m demonstrating a method more in line with the features of Vue.js.</p>



<p>The use case I&#8217;m describing here is one where some pages are public and some pages are protected via authentication. I&#8217;ve also added in authorization to the sample. So a user wanting to access protected content will log in using Keycloak (authentication) and also need the correct role assigned in Keycloak (authorization). This use case is similar to having a protected admin section of a public web site. I know there is much more to proper client than what I show here. This simple example is intended to be a starting point. This short video demonstrates the behavior we&#8217;ll code in this post.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Vue.js Keycloak Demo" width="758" height="426" src="https://www.youtube.com/embed/K5c8gzT2yr8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div></figure>



<h2 class="wp-block-heading">TLDR; The Steps</h2>



<ol class="wp-block-list"><li>Configure a Client in KeyCloak</li><li>Capture the Client ID, role name for the Client ID, and Realm name from Keycloak</li><li>Create users in Keycloak</li><li>Create a plugin in the Vue app</li><li>Integrate the plugin into app startup</li><li>Set routes to be authenticated</li><li>Create a navigation guard that manages authentication and authorization to protected resources.</li></ol>



<h2 class="wp-block-heading">Keycloak Configuration</h2>



<p>If you have not set up or configured Keycloak previously, <a rel="noreferrer noopener" href="https://davidtruxall.com/keycloak-for-local-development/(opens in a new tab)" target="_blank">check out my article demonstrating how to run Keycloak locally using Docker</a>. I&#8217;m assuming you are running a Keycloak instance, have admin rights to the Keycloak administration app and have already created a Realm. The main tasks to complete for this example are creating a Client application registration and adding users in Keycloak. If you know how to do this just <a href="#code" data-type="internal" data-id="#code">skip down to the code section</a>, otherwise let&#8217;s get to work with Keycloak.</p>



<h3 class="wp-block-heading">Create A Client in Keycloak</h3>



<p>A Client in Keycloak is a way to register an app to be secured. The Vue app we create needs to have a corresponding Client ID. Make sure to keep track of the Realm name for later in our Vue code. Click the Clients link in the left navigation pane under the Realm you are using to see the existing clients and create a new one.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="434" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client-1024x434.png" alt="Add a new client to Keycloak" class="wp-image-726" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client-1024x434.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client-300x127.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client-768x326.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-add-client.png 1056w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 1. Add a new client to Keycloak</figcaption></figure>



<p>Click the Create button at the top left of the table of existing Clients to add a new Client for your app. In my case, I&#8217;m going to create a Vue app to demo logging in with Keycloak.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="437" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client-1024x437.png" alt="Name the client and set the root URL" class="wp-image-727" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client-1024x437.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client-300x128.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client-768x327.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-name-client.png 1060w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 2. Name the client and set the root URL</figcaption></figure>



<p>Set the client id as the name of your app, and set the Root URL to the URL you are using for local development of your Vue application. In my case, the URL is http://localhost:8080. Click the Save button. Make sure you get the protocol correct. If you get an http/https mismatch you&#8217;ll have issues later on.</p>



<p>In the Client details page, make sure the Client Protocol is set to <code>openid-connect </code>and the Access Type is set to <code>public</code>. Check that the following are set properly:</p>



<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td><strong>Property</strong></td><td><strong>Value</strong></td><td><strong>Reason</strong></td></tr><tr><td>Root URL</td><td>http://localhost:8080/</td><td>Gets prepended to redirected URLS</td></tr><tr><td>Valid Redirect URIs</td><td>http://localhost:8080/*</td><td>Redirect location after logout</td></tr><tr><td>Web Origins</td><td>http://localhost:8080</td><td>Allowed origin for <a rel="noreferrer noopener" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank">CORS</a><br>(Really important for web apps)</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">Create A Role</h3>



<p>Each Client needs one or more roles. If you don&#8217;t assign roles and check them in your application, any user from your Realm will be able to log into the app. You should create roles to control user access. Click the Roles tab in your Client details page. In the Roles tab, click the Add Role button at the upper right of the table. Give your role a name and save it. We&#8217;ll need the role name for later in our Vue app.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="435" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create-1024x435.png" alt="Create a role for the new client" class="wp-image-728" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create-1024x435.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create-300x128.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create-768x327.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-role-create.png 1063w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 3. Create a role for the new client</figcaption></figure>



<h3 class="wp-block-heading">Create Users</h3>



<p>We need users to log into our application (Client). For this demo we&#8217;ll create two users. One user with the role we just created and one user without.  We will create a user and then assign a password and role. Click the Users link in the left navigation pane to get started.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="534" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add-1024x534.png" alt="Add a Keycloak user" class="wp-image-729" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add-1024x534.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add-300x156.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add-768x400.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-add.png 1055w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 4. Add a Keycloak user</figcaption></figure>



<p>You won&#8217;t see any users listed, even if you added some previously. Click the Add user button in the top right of the table.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="570" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details-1024x570.png" alt="Add user details" class="wp-image-730" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details-1024x570.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details-300x167.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details-768x427.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-details.png 1057w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 5. Add user details</figcaption></figure>



<p>Enter a username, and their actual name and email. Click the Save button. Let&#8217;s give them a password.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="512" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password-1024x512.png" alt="Set the password for the user" class="wp-image-731" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password-1024x512.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password-300x150.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password-768x384.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-password.png 1063w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 6. Set the password for the user</figcaption></figure>



<p>On the user details page, click the Credentials tab. Give the user a password. If you don&#8217;t want the user to be forced to change their password, turn the Temporary switch to off. I&#8217;ll do this when I&#8217;m running locally for development, but not when making accounts for actual users in a non-development environment. Click the Set Password button.</p>



<h3 class="wp-block-heading">Assign A Role</h3>



<p>Since the application (Client) has a role, we need to assign that role to the user so they can log into that application. Click the Role Mappings tab.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="569" src="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role-1024x569.png" alt="Assign a client role to a user" class="wp-image-732" srcset="https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role-1024x569.png 1024w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role-300x167.png 300w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role-768x427.png 768w, https://davidtruxall.com/wp-content/uploads/2021/03/keycloak-user-assign-role.png 1065w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption>Figure 7. Assign a client role to a user</figcaption></figure>



<p>Select your client created earlier in the Client Roles drop down list. The role created earlier should be there. Select that role and click the Add selected &gt; button. The user is good to go now.</p>



<p>Create a second user following the same steps, but do not assign any role to this user. Now, let&#8217;s get coding in Vue.</p>



<h2 class="wp-block-heading" id="code">Vue Setup</h2>



<p>You can find <a rel="noreferrer noopener" href="https://github.com/davetrux/vue-keycloak-demo" target="_blank">the sample code I wrote for this article at GitHub</a>. I made a simple application with the Vue-cli and edited that app.</p>



<p>We will use the library provided by Keycloak to build our authentication plumbing. It&#8217;s very important that the version of the library you install matches the version of the Keycloak instance you are working with. You can install it using npm, my Keycloak instance is version 12.0.4:</p>



<p><code>npm install --save keycloak-js@12.0.4</code></p>



<p>You can also find the JavaScript library in your Keycloak instance. It&#8217;s located at <code>&lt;yourServerUrl&gt;/auth/js/keycloak.js</code></p>



<h3 class="wp-block-heading">Create A Plugin</h3>



<p>In order to make the Keycloak object accessible throughout the application, I created a <a rel="noreferrer noopener" href="https://vuejs.org/v2/guide/plugins.html" target="_blank">Vue plugin</a>. The plugin is going to create a global <code>$keycloak</code> object we can reference anywhere in the app. The code for the plugin looks like:</p>



<pre class="wp-block-preformatted">import Vue from 'vue'<br>import <strong><em>Keycloak </em></strong>from 'keycloak-js'<br><br>const options = {<br>  url: 'http://localhost:8001/auth/',<br>  realm: 'local-dev',<br>  clientId: 'vue-demo'<br>}<br><br>const _keycloak = Keycloak(options)<br><br>const <strong><em>Plugin </em></strong>= {<br>  install(Vue) {<br>    Vue.$keycloak = _keycloak<br>  }<br>}<br><br><strong><em>Plugin</em></strong>.install = Vue =&gt; {<br>  Vue.$keycloak = _keycloak<br>  <strong><em>Object</em></strong>.defineProperties(Vue.prototype, {<br>    $keycloak: {<br>      get() {<br>        return _keycloak<br>      }<br>    }<br>  })<br>}<br><br>Vue.use(<strong><em>Plugin</em></strong>)<br><br>export default <strong><em>Plugin</em></strong></pre>



<p>The important bits to note are in the options object. The <code>url</code> must be the Keycloak base server URL using the <code>/auth/ </code>directory. The <code>realm</code> and <code>clientId</code> came from the configuration of the Client in Keycloak steps above.</p>



<p>The next step is to initialize the Keycloak object from the plugin before starting the Vue app in <code>main.js</code>. So your <code>main.js</code> file should look something like this:</p>



<pre class="wp-block-preformatted">import Vue from 'vue'<br>import App from './App.vue'<br>import <strong><em>router </em></strong>from './router'<br>import <strong><em>authentication </em></strong>from "@/plugins/authentication"<br><br>Vue.config.productionTip = false<br>Vue.use(<strong><em>authentication</em></strong>)<br><br>Vue.$keycloak<br>  .init({ checkLoginIframe: false })<br>  .then(() =&gt; {<br>    new Vue({<br>      <strong><em>router</em></strong>,<br>      render: h =&gt; h(App)<br>    }).$mount('#app')<br>  })</pre>



<p>This only sets up Keycloak. It does not yet protect secured content. If you want your app to always force a login and have no public content, change the .init function to use <code>login-required</code> upon <code>onLoad</code>:</p>



<pre class="wp-block-preformatted">.init({ onLoad: 'login-required', checkLoginIframe: false })
</pre>



<h3 class="wp-block-heading">Create A Navigation Guard</h3>



<p>In order to secure the pages we want behind a login, we have to create a <a href="https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards" target="_blank" rel="noreferrer noopener">navigation guard, a feature in Vue intended for just this purpose</a>. But, we need a way to tell which pages are the unsecured and secured pages. We do this by setting a <code>meta</code> tag in our Vue router configuration file:</p>



<pre class="wp-block-preformatted">const routes = [<br>  {<br>    path: '/',<br>    name: 'Home',<br>    component: Home,<br>    meta: {<br>      isAuthenticated: false<br>    }<br>  },<br>  {<br>    path: '/secured',<br>    name: 'Secured',<br>    meta: {<br>      isAuthenticated: true<br>    },<br>    component: () =&gt; import('../views/Secured.vue')<br>  },<br>  {<br>    path: '/unauthorized',<br>    name: 'Unauthorized',<br>    meta: {<br>      isAuthenticated: false<br>    },<br>    component: () =&gt; import('../views/Unauthorized.vue')<br>  }<br>]</pre>



<p>In the above code the home page and unauthorized message page are not secured. But the page named &#8216;Secured&#8217; is secured. The navigation guard will check this property and redirect users to login when necessary. So we create this function in the Vue router which is a global navigation guard:</p>



<pre class="wp-block-preformatted"><strong><em>router</em></strong>.beforeEach((to, from, next) =&gt; {
  if (to.meta.isAuthenticated) {
    // Get the actual url of the app, it's needed for Keycloak
    const basePath = <strong><em>window</em></strong>.location.toString()
    if (!Vue.$keycloak.authenticated) {
      // The page is protected and the user is not authenticated. Force a login.
      Vue.$keycloak.login({ redirectUri: basePath.slice(0, -1) + to.path })
    } else if (Vue.$keycloak.hasResourceRole('vue-demo-user')) {
      // The user was authenticated, and has the app role
      Vue.$keycloak.updateToken(70)
        .then(() =&gt; {
          next()
        })
        .catch(err =&gt; {
          <strong><em>console</em></strong>.error(err)
        })
    } else {
      // The user was authenticated, but did not have the correct role
      // Redirect to an error page
      next({ name: 'Unauthorized' })
    }
  } else {
    // This page did not require authentication
    next()
  }
})</pre>



<p>The navigation guard handles four use cases:</p>



<ol class="wp-block-list"><li>The page requires authentication and the user is not authenticated</li><li>The page requires authentication, the user is authenticated and has the correct role (authorized). Update their token.</li><li>The page requires authentication, the user is authenticated but not authorized. Redirect them to an error page.</li><li>The page does not require authentication</li></ol>



<p>This is all the code necessary for simple login behavior. You can see in the sample code I&#8217;ve included Login and Logout buttons and those are really one-line functions from the Keycloak object. </p>



<p>Now we have an app demonstrating simple authentication and authorization using Keycloak. There are three places we added Keycloak integration into an app: a Vue plugin, wiring up the plugin in <code>main.js</code>, and in the Vue router. The sample could certainly be expanded upon and would need more functionality, especially if the app is calling API&#8217;s. <a rel="noreferrer noopener" href="https://medium.com/js-dojo/authentication-made-easy-in-vue-js-with-keycloak-c03c7fff67bb" target="_blank">This article</a> shows how to refresh the token when using Axios using an interceptor. I hope this more Vue-centric sample helps out some folks trying to get their Vue app integrated with Keycloak. </p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>A Computer Virus and Impostor Syndrome</title>
		<link>https://davidtruxall.com/a-computer-virus-and-impostor-syndrome/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Wed, 24 Mar 2021 16:38:42 +0000</pubDate>
				<category><![CDATA[Career]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[career]]></category>
		<category><![CDATA[programming]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=604</guid>

					<description><![CDATA[2020 was the 20th anniversary of the Love Letter/Love Bug (ILOVEYOU) virus. If you&#8217;ve never heard of it, Sophos posted a good summary. I was&#8230;]]></description>
										<content:encoded><![CDATA[
<p>2020 was the 20th anniversary of the Love Letter/Love Bug (ILOVEYOU) virus. If you&#8217;ve never heard of it, <a rel="noreferrer noopener" href="https://nakedsecurity.sophos.com/2020/05/04/iloveyou-the-love-bug-virus-20-years-on-could-it-happen-again/" target="_blank">Sophos posted a good summary</a>. I was working at a New Horizons in my first job as a web developer. When it hit, I happened to be in the office of one of the IT admins. We were talking, then he looks at his screen and asks: &#8220;Why would Gary send me an email saying he loves me?&#8221;. Another similar message arrived from someone else. Then another. I saw the light bulb go off in his head. He bolted out of his chair, ran down the hall to the server room and powered off the Exchange server. The virus was in the system for only a couple minutes and had spread to around 120 accounts. This was the first time I saw disaster recovery in action. </p>



<p>For me, the most interesting moment arrived after the fact, when I got a printout of the virus code. It was written in Visual Basic for Applications (VBA). This was the same programming language I was using in my job. I was creating an intranet using Active Server Pages (<a rel="noreferrer noopener" href="https://www.bisend.com/blog/what-is-classic-asp" target="_blank">Classic ASP</a>) which used VBA. I had also been writing Word and Excel macros to automate some company processes.  When I looked at the printout, I thought to myself: &#8220;I could have written that!&#8221;. I didn&#8217;t want to write a virus, but I knew enough VBA to understand the code. It was a validating moment for me as a programmer. Someone else&#8217;s code that had done so much (even though it was damaging) was understandable to me. At that time I frequently read <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Dr._Dobb%27s_Journal" target="_blank">Dr. Dobb&#8217;s Journal</a> and those articles filled me with impostor syndrome, the programming discussed in those articles was mostly over my head. The virus code was a turning point for me.</p>



<p>Every programmer starts like this, impostor syndrome creeps around the corner frequently. There is so much to know and understand and it feels overwhelming at times. I&#8217;ve always referred to it as &#8220;<a rel="noreferrer noopener" href="https://idioms.thefreedictionary.com/drink+from+a+fire+hose" target="_blank">drinking from the firehose</a>&#8220;. Know that it gets better over time. At some point I realized I was understanding the articles in Dr. Dobbs. That didn&#8217;t happen overnight, but I did put in the work to get there. I hope your validating moment arrives, and know that I am rooting for you.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Misleading CORS Errors</title>
		<link>https://davidtruxall.com/misleading-cors-errors/</link>
		
		<dc:creator><![CDATA[Trux]]></dc:creator>
		<pubDate>Fri, 11 Dec 2020 21:02:01 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Strange Problems]]></category>
		<category><![CDATA[Tools]]></category>
		<guid isPermaLink="false">https://davidtruxall.com/?p=697</guid>

					<description><![CDATA[This post is about a problem with CORS (cross-origin resource sharing) in Chrome. CORS is one of the security mechanisms built into browsers to prevent&#8230;]]></description>
										<content:encoded><![CDATA[
<p>This post is about a problem with CORS (cross-origin resource sharing) in Chrome. CORS is one of the security mechanisms built into browsers to prevent other sites from consuming your content or APIs unless specifically allowed. If you are a native mobile developer or a back-end developer consuming APIs you may never encounter CORS errors, because CORS only applies to the browser. If you need some background on CORS, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">Mozilla has an excellent write-up</a>. I&#8217;m going to discuss an issue I&#8217;ve encountered with misleading CORS errors in Chrome and ways to work around the error to discover the actual problem.</p>



<p>At some point Chrome changed the way CORS is reported in the developer tools, perhaps as far back as 2019. The current behavior in Chrome is that CORS errors take precedence over network errors. So today if your front-end application has a problem with the back-end service, it might report the problem as CORS when it is actually something else entirely. <a href="https://stackoverflow.com/questions/54795541/503-return-from-server-is-branded-as-cors-violation-by-chrome">I am not the only developer that encountered this problem</a>. If you read the error message (good developers read errors closely, don&#8217;t they?), you will end up going down the wrong path to solve the problem. Here&#8217;s the error I see in Chrome:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>Access to XMLHttpRequest at &#8216;https://my.long.url.com&#8217; from origin &#8216;http://localhost:8080&#8217; has been blocked by CORS policy: No &#8216;Access-Control-Allow-Origin&#8217; header is present on the requested resource.</p></blockquote>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="249" src="https://davidtruxall.com/wp-content/uploads/2020/12/chrome-cors-1024x249.png" alt="The Chrome developer tools show only a CORS error in the console" class="wp-image-699" srcset="https://davidtruxall.com/wp-content/uploads/2020/12/chrome-cors-1024x249.png 1024w, https://davidtruxall.com/wp-content/uploads/2020/12/chrome-cors-300x73.png 300w, https://davidtruxall.com/wp-content/uploads/2020/12/chrome-cors-768x187.png 768w, https://davidtruxall.com/wp-content/uploads/2020/12/chrome-cors.png 1266w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>It looks like a CORS error, but the underlying issue ended up being  something else entirely. In my case, the problem was that the service layer was down. There is a reverse proxy  in front of the service, and the reverse proxy was correctly returning an <a href="https://httpstatuses.com/503">HTTP 503 response</a>, because the service application behind the proxy had crashed. But that HTTP response lacked the <code>Access-Control-Allow-Origin</code> header. It was missing because the underlying application was setting those headers, not the reverse proxy. Since the application was down, the headers were not set. This causes Chrome to display the CORS error in the developer console instead of the network error.</p>



<h2 class="wp-block-heading">Finding the Actual Problem</h2>



<p>Because I was confident that there was no CORS misconfiguration in the server application, I had to look elsewhere for the answer. My first choice is to try a different browser. Another browser sometimes displays the same errors differently. I tried Safari. Same result. Edge? Same result. FireFox? A-ha! I saw the 503 error in the JavaScript console and also in the network tab in FireFox.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="190" src="https://davidtruxall.com/wp-content/uploads/2020/12/firefox-cors-1024x190.png" alt="The FireFox developer tools show both the 503 HTTP error code and the CORS error in the console" class="wp-image-700" srcset="https://davidtruxall.com/wp-content/uploads/2020/12/firefox-cors-1024x190.png 1024w, https://davidtruxall.com/wp-content/uploads/2020/12/firefox-cors-300x56.png 300w, https://davidtruxall.com/wp-content/uploads/2020/12/firefox-cors-768x142.png 768w, https://davidtruxall.com/wp-content/uploads/2020/12/firefox-cors.png 1245w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>FireFox also reported the CORS error along with the network error. The JavaScript code still failed and kept the content of the 503 response from reaching the application code, which is the expected behavior for CORS errors. But that information was in the developer console. This is much more helpful to me as a developer. I assume that Chrome and the other browsers view this as a security issue and just deny the request when the header is missing, even in the developer console.</p>



<p>Another approach to finding the real issue would be to use a tool not affected by CORS, such as <code>curl</code> or PostMan. I used <code>curl</code> adding the <code>-v</code> flag and see the 503 response in the headers returned from the call:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="655" height="247" src="https://davidtruxall.com/wp-content/uploads/2020/12/curl-cors.png" alt="The curl command-line tool shows a 503 error response in the HTTP headers" class="wp-image-703" srcset="https://davidtruxall.com/wp-content/uploads/2020/12/curl-cors.png 655w, https://davidtruxall.com/wp-content/uploads/2020/12/curl-cors-300x113.png 300w" sizes="(max-width: 655px) 100vw, 655px" /></figure>



<p>As another alternative, I could even start an instance of Chrome without the CORS protections enabled using the terminal on my Mac :</p>



<p><code>open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security</code></p>



<p>This also worked and I could now see the 503 error in the Chrome developer console. I do not like to develop using Chrome with security disabled because it ultimately hides issues like CORS which can occur as legitimate errors. But it&#8217;s a good tool for troubleshooting.</p>



<p>Perhaps we could fix the problem by configuring the reverse proxy to add the missing <code>Access-Control-Allow-Origin</code> in the case of 5xx HTTP responses? Or maybe configure the reverse proxy to add the CORS-related headers all the time instead of the underlying application? I&#8217;m not sure, finding information about this situation has been difficult.</p>



<p>The bottom line is this: If you see a CORS error when there were none previously and believe CORS is configured correctly on the back end, use some other tool to ensure you are troubleshooting the correct error.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
