mauriciovigolo / keycloak-angular

Easy Keycloak setup for Angular applications.
MIT License
714 stars 271 forks source link

Problem with KeycloakService in Angular 17 #535

Closed falconsapsan closed 6 months ago

falconsapsan commented 6 months ago
- [X] bug report -> please search for issues before submitting
- [ ] feature request

Versions.

keycloak-angular: 15.0.0 keycloak-js: 23.0.3 keycloak: 23.0.0 angular: 17.0.7

Repro steps.

app.config.ts

function initializeKeycloak(keycloak: KeycloakService) {
    return () =>
        keycloak.init({
            config: {
                url: environment.keycloak.url,
                realm: environment.keycloak.realm,
                clientId: environment.keycloak.clientId,
            },
            initOptions: {
                onLoad: 'check-sso',
                silentCheckSsoRedirectUri:
                    window.location.origin + '/assets/silent-check-sso.html',
            },
            bearerExcludedUrls: ['/assets'],
            shouldUpdateToken(request) {
                return request.headers.get('token-update') !== 'false';
            },
            loadUserProfileAtStartUp: true,
        });
}

export const appConfig: ApplicationConfig = {
    providers: [
        provideHttpClient(
            withInterceptors([
                authInterceptor,
            ]),
            withInterceptorsFromDi()
        ),
        importProvidersFrom(KeycloakAngularModule),
        KeycloakBearerInterceptor,
        {
            provide: HTTP_INTERCEPTORS,
            useClass: KeycloakBearerInterceptor,
            multi: true,
        },
        {
            provide: APP_INITIALIZER,
            useFactory: initializeKeycloak,
            multi: true,
            deps: [KeycloakService]
        },
        provideRouter(routes),
    ],
};

main.ts

bootstrapApplication(
    AppComponent, appConfig)
    .catch((err) => console.error(err));

start.component.ts


@Component({
    selector: 'app-start',
    standalone: true,
    imports: [],
    providers: [],
    templateUrl: './start.component.html',
    styleUrl: './start.component.css',
})
export class StartComponent implements OnInit, OnDestroy {
    private _unsubscribeAll: Subject<any> = new Subject<any>();
    isLoggedIn: boolean = false;

    constructor(
        private _httpClient: HttpClient,
        private readonly keycloak: KeycloakService
    ) {}

    ngOnDestroy(): void {
        this._unsubscribeAll.next(null);
        this._unsubscribeAll.complete();
    }

     ngOnInit()  {
         console.log('isLoggedIn()', this.keycloak.isLoggedIn())
         console.log('getKeycloakInstance()', this.keycloak.getKeycloakInstance())
        if (this.isLoggedIn) {
            this.keycloak.loadUserProfile().then(user => {
                console.log(user)
            }).catch(err => {
                console.log(err)
            })
        }
    }
}

auth.guard.ts

import { Injectable } from '@angular/core';
import {
    ActivatedRouteSnapshot,
    Router,
    RouterStateSnapshot,
    UrlTree,
} 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);
    }

    async isAccessAllowed(
        route: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
    ): Promise<boolean | UrlTree> {
        if (!this.authenticated) {
            await this.keycloak.login({
                redirectUri: window.location.origin + state.url,
            });
        } else {
            // console.log('authenticated', this.keycloak.loadUserProfile());
            // console.log('authenticated', this.keycloak.isLoggedIn());
        }
        return this.authenticated;
    }
}

The log given by the failure.

I have a component named start.component.ts in which I want to get the authentication status and the profile of the authenticated user." In this component, isLoggedIn is always false, and getKeycloakInstance is always undefined.

isLoggedIn() false
getKeycloakInstance() undefined

The AuthGuard, meanwhile, works correctly; inside it, there's always getKeycloakInstance() available, and it's possible to get the user's profile. Please suggest ways to solve this problem.

falconsapsan commented 6 months ago

My mistake was that I had also included the KeycloakAngularModule in the import section of the AppComponent. With that configuration, everything is working.


    providers: [
        provideHttpClient(
            withInterceptors([
                authInterceptor,
            ]),
            withInterceptorsFromDi()
        ),
        KeycloakAngularModule, 
        importProvidersFrom(KeycloakAngularModule),
        KeycloakBearerInterceptor,
        {
            provide: HTTP_INTERCEPTORS,
            useClass: KeycloakBearerInterceptor,
            multi: true,
        },
        {
            provide: APP_INITIALIZER,
            useFactory: initializeKeycloak,
            multi: true,
            deps: [KeycloakService]
        },
        provideRouter(routes),
    ],
};```