title: Keycloak 8 for Angular 8 / Java app: get error "An error occurred, please login again.." when login. All Dockerized on vps with Nginx, ssl, https, dns
body: <p>Firstly, I inform you that i'm a junior developer. So I beg for your tolerance regarding to my question :)</p>
  
  <p>What I'm trying to do is a simple prod POC with Keycloak for an Angular 8 frontend with a Java backend and a Postgres db. I'm using an OVH VPS, with Nginx / Certbot for the ssl and https management.</p>
  
  <p><strong>What is not working:</strong>
   - access to a private url (protected by keycloak, using [AppAuthGuard] in my Angular app : Keycloak correctly redirect on the Login page, but the login doesn't work. <strong>I get an error from Keycloak : "We are sorry... An error occurred, please login again through your application.".</strong></p>
  
  <p>My browser says : "Status Code: 400 Bad Request"
  And when I check my Keycloak container logs, I have a warning: </p>
  
  <pre class="lang-sh prettyprint-override"><code>WARN  [org.keycloak.events] (default task-1) type=LOGIN_ERROR, realmId=TheLibrary, clientId=null, userId=null, ipAddress=192.168.80.1, error=invalid_code
  </code></pre>
  
  <p>That being said, my urls does have realm and clientId where it must be...</p>
  
  <p><strong>What is working fine:</strong>
   - url redirection using subdomain I created on my VPS,
   - access to my Keycloak admin console,
   - access to a public url of my Frontend (I mean a url without [AppAuthGuard])
   - everything, if I run my backend / frontend / Keycloak code in local environment, without ssl or https.</p>
  
  <p>I'm struggling on that login error since 4 days. It's driving me crazy.. and I have pretty much no clue to investigate.</p>
  
  <p>I did't find any similar issue online.</p>
  
  <p>The only thing I noticed, as a difference between my local poc which is working fine (with a local standalone Keycloak), is a cookie that appears to be loaded after a successful Login, and it is not loaded when I get the error in my prod poc (with everything online on my vps).
  I can see it with developer mode in Chrome > Network > all > ... </p>
  
  <p>Here a my code and configuration :</p>
  
  <p><strong><em>FRONTEND</em></strong></p>
  
  <p><strong>app-routing.module.ts</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>import {NgModule} from '@angular/core';
  import {RouterModule, Routes} from '@angular/router';
  import {BooksComponent} from './shared/books/books.component';
  import {AppAuthGuard} from './app-auth.guard';
  
  const routes: Routes = [
    {
      path: 'books', component: BooksComponent
    },
    {
      path: 'search',
      loadChildren: () =&gt; import('./search/search.module').then(mod =&gt; mod.SearchModule),
      // canActivate: [AppAuthGuard]
    },
    {
      path: 'last-release',
      loadChildren: () =&gt; import('./last-release/last-release.module').then(mod =&gt; mod.LastReleaseModule),
      // canActivate: [AppAuthGuard]
    },
    {
      path: 'loan',
      loadChildren: () =&gt; import('./loan/loan.module').then(mod =&gt; mod.LoanModule),
      canActivate: [AppAuthGuard],
      data: { roles: ['user'] }
    },
    {
      path: '', redirectTo: '/books',
      pathMatch: 'full'
    },
    {
      path: '**',
      redirectTo: '/'
    }
  ];
  
  @NgModule({
    declarations: [],
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
  })
  export class AppRoutingModule { }
  </code></pre>
  
  <p><strong>app-auth.guard.ts</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>import {Injectable} from '@angular/core';
  import {ActivatedRouteSnapshot, Router} from '@angular/router';
  import {KeycloakAuthGuard, KeycloakService} from 'keycloak-angular';
  
  @Injectable({
    providedIn: 'root'
  })
  export class AppAuthGuard extends KeycloakAuthGuard {
  
    constructor(protected router: Router,
                protected keycloakAngular: KeycloakService) {
        super(router, keycloakAngular);
      }
  
    isAccessAllowed(route: ActivatedRouteSnapshot): Promise&lt;boolean&gt; {
      return new Promise(async (resolve, reject) =&gt; {
        const requiredRoles = route.data.roles;
  
        console.log('Class: AppAuthGuard, Function: , Line 19 (): '
        , route);
  
        if (route.url[0].path === '/books') {
          return resolve(true);
        } else if (!this.authenticated ) {
          this.keycloakAngular.login();
          return resolve(true);
        }
        if (!requiredRoles || requiredRoles.length === 0) {
          return resolve(true);
        } else {
          if (!this.roles || this.roles.length === 0) {
            resolve(false);
          }
          let granted = false;
          for (const requiredRole of requiredRoles) {
            if (this.roles.indexOf(requiredRole) &gt; -1) {
              granted = true;
              break;
            }
          }
          resolve(granted);
        }
      });
    }
  }
  </code></pre>
  
  <p><strong>app.module.ts</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>import {BrowserModule} from '@angular/platform-browser';
  import {APP_INITIALIZER, ApplicationRef, DoBootstrap, NgModule} from '@angular/core';
  import {AppComponent} from './app.component';
  import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
  import {AppRoutingModule} from './app-routing.module';
  import {BooksComponent} from './shared/books/books.component';
  import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
  import {ReactiveFormsModule} from '@angular/forms';
  import {KeycloakAngularModule, KeycloakService} from 'keycloak-angular';
  import {environment} from '../environments/environment';
  import {HttpErrorInterceptor} from './shared/http-error.interceptor';
  import {HeaderComponent} from './shared/header/header.component';
  import {SideNavComponent} from './shared/side-nav/side-nav.component';
  import {initializer} from './app.init';
  
  const keycloakService = new KeycloakService();
  
  @NgModule({
    declarations: [
      AppComponent,
      BooksComponent,
      HeaderComponent,
      SideNavComponent
    ],
    imports: [
      BrowserModule,
      BrowserAnimationsModule,
      HttpClientModule,
      AppRoutingModule,
      ReactiveFormsModule,
      KeycloakAngularModule
    ],
    providers: [
      {
        provide: KeycloakService,
        useValue: keycloakService
      }
    ],
    entryComponents: [AppComponent]
  })
  export class AppModule implements DoBootstrap {
    ngDoBootstrap(appRef: ApplicationRef) {
      keycloakService
        .init({
          config: environment.keycloak,
          initOptions: {
            checkLoginIframe: false
          },
        })
        .then(() =&gt; {
          console.log('[ngDoBootstrap] bootstrap app');
          appRef.bootstrap(AppComponent);
        })
        .catch(error =&gt; console.error('[ngDoBootstrap] init Keycloak failed', error));
    }
  }
  </code></pre>
  
  <p><strong>environment.prod.ts</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>export const baseUrls = {
    catalog: 'https://thelibrary.ms.catalog.mypoc.online/api'
  };
  
  export const environment = {
    production: true,
    authServiceApiUrl: 'https://auth.thelibrary.mypoc.online/auth',
    keycloak: {
      url: 'https://auth.thelibrary.mypoc.online/auth',
      realm: 'TheLibrary',
      clientId: 'thelibrary-app',
      'ssl-required': 'all',
      'public-client': true
    },
    baseUrl: {
      catalog: {
        getBooks: baseUrls.catalog + '/books'
      }
    }
  };
  </code></pre>
  
  <p><strong>Dockerfile-front</strong></p>
  
  <pre class="lang-sh prettyprint-override"><code># base image
  FROM node:latest
  
  # install chrome for protractor tests
  RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
  RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" &gt;&gt; /etc/apt/sources.list.d/google.list'
  RUN apt-get update &amp;&amp; apt-get install -yq google-chrome-stable
  
  # set working directory
  WORKDIR /app
  
  # add `/app/node_modules/.bin` to $PATH
  ENV PATH /app/node_modules/.bin:$PATH
  
  # install and cache app dependencies
  COPY build/front/package.json /app/package.json
  RUN npm install
  RUN npm install -g @angular/cli@8.3.24
  
  # add app
  COPY build/front/. /app
  
  # start app
  RUN pwd
  RUN ls
  
  CMD ng serve --disable-host-check --configuration=production --proxy-config proxy.conf.json --host 0.0.0.0 --public-host https://www.thelibrary.mypoc.online
  </code></pre>
  
  <p>Bytheway, is my "ng serve" line ok? </p>
  
  <p><strong>Docker-compose.yml</strong></p>
  
  <pre><code>
  version: '3.7'
  
  services:
      db-thelibrary:
          build:
              context: ./db
              dockerfile: Dockerfile-db
          container_name: cont-db-thelibrary
          restart: unless-stopped
          ports:
              - 5432:5432
          environment:
              POSTGRES_DB: ########
              POSTGRES_USER: ########
              POSTGRES_PASSWORD: ########
          volumes:
              - db_data:/var/lib/postgres/data
          networks:
              - network-thelibrary
  
      back-thelibrary:
          depends_on:
              - db-thelibrary
          build:
              context: ./build/back
              dockerfile: Dockerfile
          container_name: cont-back-thelibrary
          ports:
              - '127.0.0.1:8090:8090'
          environment:
              SPRING_PROFILES_ACTIVE: prod
              SPRING_DATASOURCE_USERNAME: ########
              SPRING_DATASOURCE_PASSWORD: ########
              SPRING_DATASOURCE_URL: jdbc:postgresql://db-thelibrary:5432/db_thelibrary?currentSchema=dev
          networks:
              - network-thelibrary
  
      front-thelibrary:
          build:
              context: build/front
              dockerfile: Dockerfile-front
          container_name: cont-front-thelibrary
          ports:
              - '127.0.0.1:4200:4200'
          networks:
              - network-thelibrary
  
  networks:
      network-thelibrary:
          driver: bridge
  
  volumes:
      front:
      db_data:
  </code></pre>
  
  <p><strong><em>KEYCLOAK</em></strong></p>
  
  <p><strong>Docker-compose.yml</strong></p>
  
  <pre><code>
  version: '3.7'
  
  services:
  
      db-keycloak:
          image: postgres
          container_name: cont-db-keycloak
          restart: always
          volumes:
              - postgres_data:/var/lib/postgresql/data
          environment:
              POSTGRES_DB: #######
              POSTGRES_USER: ########
              POSTGRES_PASSWORD: #########
          networks:
             - network-keycloak
  
      keycloak:
          depends_on:
              - db-keycloak
          image: jboss/keycloak
          container_name: cont-keycloak
          environment:
              KEYCLOAK_HOSTNAME: www.auth.thelibrary.mypoc.online
              KEYCLOAK_ALWAYS_HTTPS: 'true'
              PROXY_ADDRESS_FORWARDING: 'true'
              KEYCLOAK_LOGLEVEL: DEBUG
              KEYCLOAK_USER: ######
              KEYCLOAK_PASSWORD: ##########
              DB_VENDOR: POSTGRES
              DB_ADDR: db-keycloak
              DB_PORT: 5432
              DB_DATABASE: keycloak
              DB_USER: keycloak
              DB_SCHEMA: public
              DB_PASSWORD: ###
              # Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
  #            JDBC_PARAMS: "ssl=true"
          command:
              -Djboss.socket.binding.port-offset=001
          ports:
              - '127.0.0.1:8081:8081'
              - '127.0.0.1:8443:8443'
          volumes:
              - config:/config/
          networks:
              - network-keycloak
  
  networks:
      network-keycloak:
          driver: bridge
  
  volumes:
      config:
          driver: local
          driver_opts:
              type: 'none'
              o: 'bind'
              device: '/srv/keycloak/config'
      postgres_data:
          driver: local
          driver_opts:
              type: 'none'
              o: 'bind'
              device: '/srv/keycloak/postgres_data'
  </code></pre>
  
  <p><strong>Keycloak client settings :</strong></p>
  
  <pre><code>- Realm : TheLibrary
  - Client ID : thelibrary-app
  - Root URL : https://www.thelibrary.mypoc.online
  - Valid Redirect URIs : https://www.thelibrary.mypoc.online/*
  - Base URL : https://www.thelibrary.mypoc.online
  - Web Origins : https://www.thelibrary.mypoc.online
  </code></pre>
  
  <p>Of course I correctly created two role, admin (composite role, including user) and user, linked to my client.</p>
  
  <p><strong><em>NGINX</em></strong></p>
  
  <p>My VPS run with Debian 9 Stretch. I installed Nginx and Certbot on it, with very basic settings, to handle that <a href="https://my.sub.url" rel="noreferrer">https://my.sub.url</a> are well redirected on <a href="https://ipAdress:port" rel="noreferrer">https://ipAdress:port</a>. 
  I followed this link to do so: <a href="https://certbot.eff.org/lets-encrypt/debianstretch-nginx" rel="noreferrer">https://certbot.eff.org/lets-encrypt/debianstretch-nginx</a>
  I created the following .conf files in /etc/nginx/sites-available/</p>
  
  <p><strong>nginx.conf</strong></p>
  
  <pre class="lang-py prettyprint-override"><code>user www-data;
  worker_processes auto;
  pid /run/nginx.pid;
  include /etc/nginx/modules-enabled/*.conf;
  
  events {
      worker_connections 768;
  }
  
  http {
  
      ##
      # Basic Settings
      ##
  
      sendfile on;
      tcp_nopush on;
      tcp_nodelay on;
      keepalive_timeout 65;
      types_hash_max_size 2048;
  
      include /etc/nginx/mime.types;
      default_type application/octet-stream;
  
      ##
      # SSL Settings
      ##
  
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
      ssl_prefer_server_ciphers on;
  
      ##
      # Logging Settings
      ##
  
      access_log /var/log/nginx/access.log;
      error_log /var/log/nginx/error.log;
  
      ##
      # Gzip Settings
      ##
  
      gzip on;
      gzip_disable "msie6";
  
      ##
      # Virtual Host Configs
      ##
  
      include /etc/nginx/conf.d/*.conf;
      include /etc/nginx/sites-enabled/*;
  }
  </code></pre>
  
  <p><strong>thelibrary.conf</strong></p>
  
  <pre class="lang-py prettyprint-override"><code>
  server {
  
      server_name www.thelibrary.mypoc.online thelibrary.mypoc.online;
  
      location / {
        proxy_pass http://127.0.0.1:4200;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        add_header Set-Cookie cip=$remote_addr;
        add_header Set-Cookie chost=$Host;
      }
  
  
      listen [::]:443 ssl ipv6only=on; # managed by Certbot
      listen 443 ssl; # managed by Certbot
      ssl_certificate /etc/letsencrypt/live/thelibrary.mypoc.online/fullchain.pem; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/thelibrary.mypoc.online/privkey.pem; # managed by Certbot
      include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
      ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
  }
  
  server {
      if ($host = www.thelibrary.mypoc.online) {
          return 301 https://$host$request_uri;
      } # managed by Certbot
  
  
      if ($host = thelibrary.mypoc.online) {
          return 301 https://$host$request_uri;
      } # managed by Certbot
  
  
      listen 80;
      listen [::]:80;
  
      server_name www.thelibrary.mypoc.online thelibrary.mypoc.online;
      return 404; # managed by Certbot
  }
  </code></pre>
  
  <p><strong>keycloak.conf</strong></p>
  
  <pre class="lang-py prettyprint-override"><code>server {
      server_name www.auth.thelibrary.mypoc.online auth.thelibrary.mypoc.online;
  
      location / {
        proxy_pass http://127.0.0.1:8081;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        add_header Set-Cookie cip=$remote_addr;
        add_header Set-Cookie chost=$Host;
      }
  
      listen [::]:443 ssl; # managed by Certbot
      listen 443 ssl; # managed by Certbot
      ssl_certificate /etc/letsencrypt/live/thelibrary.mypoc.online/fullchain.pem; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/thelibrary.mypoc.online/privkey.pem; # managed by Certbot
      include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
      ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
  }
  
  server {
      if ($host = www.auth.thelibrary.mypoc.online) {
          return 301 https://$host$request_uri;
      } # managed by Certbot
  
  
      if ($host = auth.thelibrary.mypoc.online) {
          return 301 https://$host$request_uri;
      } # managed by Certbot
  
  
      listen 80;
      listen [::]:80;
  
      server_name www.auth.thelibrary.mypoc.online auth.thelibrary.mypoc.online;
      return 404; # managed by Certbot
  }
  </code></pre>
  
  <p>Each of my components runs with Docker compose.</p>
  
  <p>For more details, here is my github : <a href="https://github.com/TheLibraryGroup" rel="noreferrer">https://github.com/TheLibraryGroup</a>.
  It contains most of my config files.</p>
  
  <p>It would be very nice if someone could help me. I'm pretty sure it's a very little thing to do, but I can't figure it out since I'm really new to devops and networks topic.</p>
  
  <p><strong>Thank you very much for you time!</strong></p>