Endless loop authorization endpoint for Keycloak Nginx

Discussão em 'Angular' iniciado por kawlorridor, Janeiro 6, 2025.

  1. kawlorridor

    kawlorridor Guest

    I have an application using Angular, Keycloak, and Nginx. Nginx is configured to handle reverse proxy and SSL termination. The application worked fine before enabling SSL termination. After enabling it, I encounter the following issue:

    1. I visit the application, and it redirects me to the Keycloak login page.
    2. I enter my credentials, and the application redirects me to the homepage (works fine up to this point).
    3. If I reload the page or navigate to another page, the application enters a loop. The console log shows repeated requests to:
    • https://domain/realms/realm/protocol/openid-connect/3p-cookies/step1.html
    • https://domain/realms/realm/protocol/openid-connect/3p-cookies/step2.html

    Additionally:

    • If I refresh the page before logging in, the same loop occurs.
    • In the network tab, I observe that a request to https://domain/realms/realm/protocol/openid-connect/auth?client_id returns a 200 response (HTML file) instead of the expected 302 redirect. This is followed by step1 and step2 requests.
    • Cookies AUTH_SESSION_ID, KEYCLOAK_SESSION, KEYCLOAK_IDENTITY, and their legacy versions are saved in the browser.

    Technical Details:

    • Angular: 18
    • Keycloak Angular: ^16.1.0
    • Keycloak JS: ^25.0.6
    • Keycloak Docker Version: quay.io/keycloak/keycloak:25.0.6

    Relevant Code:

    Angular Configuration (app.module.ts):

    export function initializeKeycloak(keycloak: KeycloakService) {
    return () =>
    keycloak.init({
    config: {
    url: 'https://domain',
    realm: 'realm',
    clientId: 'realm-client',
    },
    initOptions: {
    onLoad: 'check-sso',
    silentCheckSsoRedirectUri: window.location.origin + '/assets/silent-check-sso.html',
    checkLoginIframe: false,
    },
    enableBearerInterceptor: true,
    bearerExcludedUrls: ['/assets', '/clients/public'],
    });
    }



    Authguard

    import { Injectable } from '@angular/core';
    import {
    ActivatedRouteSnapshot,
    Router,
    RouterStateSnapshot,
    } from '@angular/router';
    import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';

    @Injectable({
    providedIn: 'root',
    })
    export class AuthGuard extends KeycloakAuthGuard {
    constructor(
    protected override readonly router: Router,
    protected readonly keycloak: KeycloakService
    ) {
    super(router, keycloak);
    }

    public async isAccessAllowed(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
    ) {
    // Force the user to log in if currently unauthenticated.
    if (!this.authenticated) {
    console.log("authguard: not auth");
    await this.keycloak.login({
    redirectUri: window.location.origin + state.url
    });
    }

    // Get the roles required from the route.
    const requiredRoles = route.data['roles'];
    console.log("authguard: auth 1");
    // Allow the user to to proceed if no additional roles are required to access the route.
    if (!(requiredRoles instanceof Array) || requiredRoles.length === 0) {
    return true;
    }

    // Allow the user to proceed if all the required roles are present.
    console.log("authguard: auth 2");
    return requiredRoles.every((role) => this.roles.includes(role));
    }
    }


    Nginx Configuration:

    events {
    worker_connections 1024;
    }

    http {

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    server {
    listen 443 ssl;
    server_name domain;

    # SSL certificates
    ssl_certificate /etc/pki/tls/domain.crt;
    ssl_certificate_key /etc/pki/tls/domain.rsa;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;

    root /var/www/html/dist;
    index index.html;

    # Frontend Configuration
    location / {
    try_files $uri /index.html;
    }

    # Backend API Proxy
    location /api/ {
    proxy_pass ip;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Authorization $http_authorization;
    }

    #Keycloak Proxy
    location /realms/ {
    proxy_pass http://ip;
    proxy_set_header Host $host:$server_port;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_pass_header Set-Cookie;
    }

    #Keycloak Proxy
    location /resources/ {
    proxy_pass http://ip;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_set_header X-Forwarded-Proto $scheme;
    }

    }
    }


    Keycloak Docker Configuration:

    services:
    keycloak:
    image: quay.io/keycloak/keycloak:25.0.6
    container_name: keycloak
    environment:
    KEYCLOAK_IMPORT: /opt/keycloak/data/import/realm.json
    KC_HTTP_ENABLED: 'true'
    KC_HTTP_PORT: 8081
    KC_PROXY_HEADERS: xforwarded
    PROXY_ADDRESS_FORWARDING: 'true'
    KC_HOSTNAME_STRICT: 'false'


    Question: What could be causing this redirect loop after enabling SSL termination? How can I resolve it while maintaining secure SSL termination?

    Continue reading...

Compartilhe esta Página