Docker to Podman migration

Tagged docker, podman, host  Languages 

The host alias host.docker.internal does not exist in podman and must be renamed to host.containers.internal.

How to fix "invalid_grant" thrown by the Rails doorkeeper gem

Tagged oauth2, invalid_grant, doorkeeper  Languages bash, ruby

The doorkeeper gem is throwing invalid_grant in your face and you don’t know why. Fixing it will be easier if we add some debug statements to the doorkeeper gem.

To find the relevant code, we run this from the CLI:

bundle exec gem which doorkeeper

This gives us the location of the doorkeeper code.

By reading or running grep on the doorkeeper code we can see that the code that raises the invalid_grant error is one of these lines in Doorkeeper::OAuth::AuthorizationCodeRequest:

  validate :grant,        error: :invalid_grant
  # @see
  validate :redirect_uri, error: :invalid_grant
  validate :code_verifier, error: :invalid_grant

For details, see:

Add a debugger statement (binding.pry) to each validation method in gems/doorkeeper-5.5.4/lib/doorkeeper/oauth/authorization_code_request.rb:

    78: def validate_redirect_uri
    79:   binding.pry
 => 80:   Helpers::URIChecker.valid_for_authorization?(
    81:     redirect_uri,
    82:     grant.redirect_uri,
    83:   )
    84: end

The debugger will stop when we try to authenticate again, and we find out that the problem in this case is an extra query parameters in the redirect_uri:

[1] pry(#<Doorkeeper::OAuth::AuthorizationCodeRequest>)> redirect_uri
=> "http://localhost:3000/app/oauth_session?source=password"
[2] pry(#<Doorkeeper::OAuth::AuthorizationCodeRequest>)> grant.redirect_uri
=> "http://localhost:3000/app/oauth_session?xxx=yyy&source=password"

By reading the OAuth specification we find that parameters are not allowed:

The solution is to remove any extra parameters, in this case xxx, from the client application’s redirect URI.

OAuth2 errors

Tagged oauth2, error, invalid_request, invalid_grant  Languages bash

OAuth2 errors

The list of OAuth2 errors can be found from the RFC:

       The request is missing a required parameter, includes an
       unsupported parameter value (other than grant type),
       repeats a parameter, includes multiple credentials,
       utilizes more than one mechanism for authenticating the
       client, or is otherwise malformed.

       Client authentication failed (e.g., unknown client, no
       client authentication included, or unsupported
       authentication method).  The authorization server MAY
       return an HTTP 401 (Unauthorized) status code to indicate
       which HTTP authentication schemes are supported.  If the
       client attempted to authenticate via the "Authorization"
       request header field, the authorization server MUST
       respond with an HTTP 401 (Unauthorized) status code and
       include the "WWW-Authenticate" response header field
       matching the authentication scheme used by the client.

       The provided authorization grant (e.g., authorization
       code, resource owner credentials) or refresh token is
       invalid, expired, revoked, does not match the redirection
       URI used in the authorization request, or was issued to
       another client.

       The authenticated client is not authorized to use this
       authorization grant type.

       The authorization grant type is not supported by the
       authorization server.


How to debug and fix chromedriver and selenium errors

Tagged chromedriver, capybara, connection, refused, selenium  Languages bash

You an enable the chromedriver log to debug and fix chromedriver and Selenium errors such as “unknown error: net::ERR_CONNECTION_REFUSED”.

First start the chromedriver in the background:

nohup chromedriver --port=12345 --verbose --log-path=/tmp/chromedriver.log &

Then configure Selenium to use the chromedriver instance:, browser: :chrome, options: ..., url: 'http://localhost:12345')

Next run the tests and inspect the chromedriver log.

In my case the issue was that an unreachable URL, see the unreachableUrl property in the output below:

[1659101781.048][DEBUG]: DevTools WebSocket Event: Page.frameNavigated B30A4B9FE0908FDACE0522341267520A {
   "frame": {
      "adFrameStatus": {
         "adFrameType": "none"
      "crossOriginIsolatedContextType": "NotIsolated",
      "domainAndRegistry": "",
      "gatedAPIFeatures": [  ],
      "id": "B30A4B9FE0908FDACE0522341267520A",
      "loaderId": "595BC7FEFD2C1ECCE31157FF7972F889",
      "mimeType": "text/html",
      "secureContextType": "InsecureScheme",
      "securityOrigin": "://",
      "unreachableUrl": "http://localhost:3030/",
      "url": "chrome-error://chromewebdata/"
   "type": "Navigation"


Shadow DOM and shadow root

Tagged shadowroot, root, shadow, dom, attachshadow  Languages javascript

Shadow DOM is very important for web components because it allows specific sections of the HTML document to be completely isolated from the rest of the document. This means CSS styles that are applied to the DOM are not applied to the Shadow DOM, and vice versa.

The shadow DOM can only be accessed through the reference returned by attachShadow:

const shadowComponent =  document.getElementById('component').attachShadow({mode: 'open'}); 
const p = document.createElement('p'); 
p.setAttribute('class', 'title'); 
p.innerText = 'Lorem ipsum'; 

The shadow root can be styled with CSS:

const css = document.createElement('style'); 
css.innerText = ` 
  .title { 
      font-size: 2rem; 
      font-weight: bolder; 

Use templates and slots make it easier to create components:


Also see the Awesome Styled Components repository and the Styled Components project for inspiration:

Details HTML element with open attribute

Tagged no-javascript, details, collapsible, open  Languages html

The details HTML element can be used as a no-Javascript alternative to e.g. the Bootstrap collapsible element:

<details open>
  <summary>This is always shown</summary>
  <p>This is hidden.</p>

Pigeonhole principle

Tagged hash, sha256, md5, crc, cryptography  Languages 

When working with hashing functions and cryptography the pigeonhole principle is essential knowledge:

In mathematics, the pigeonhole principle states that if n items are put into m containers, with n > m, then at least one container must contain more than one item.

For example, given that the population of London is greater than the maximum number of hairs that can be present on a human’s head, then the pigeonhole principle requires that there must be at least two people in London who have the same number of hairs on their heads.

This means that in order to create a completely collision-free hashing function, every message would have to have a hashed output of the same length as the input.

Simple backup script with GPG encryption

Tagged gpg, tar, backup  Languages bash

Save to a file named backup and run chmod +x backup:

# Uses tar to store files in one TAR file.
# Uses gpg to encrypt the TAR file.
# Example:
# ./backup /folder-to-backup
export DIR_NAME=$1
export BACKUP_DATE=`date +"%Y%m%d-%H%M"`
# --exclude="*.log"
# -r = recipient (only one who can decrypt)
gpg --encrypt -r [email protected] $BACKUP_FILE

EXPLAIN ANALYZE with ActiveRecord

Tagged explain, activerecord, analyze  Languages ruby

ActiveRecord example:

module ActiveRecord
  module ConnectionAdapters
    module PostgreSQL
      module DatabaseStatements
        def explain_analyze(arel, binds = [])
          sql = "EXPLAIN ANALYZE #{to_sql(arel, binds)}"
, "EXPLAIN", binds))

puts Job.where("slow = true").explain_analyze

Raw SQL example:

puts ActiveRecord::Base.connection.select_all(sql) { |x| x.fetch("QUERY PLAN") }.join("\n")