mauriciovigolo / keycloak-angular

Easy Keycloak setup for Angular applications.
MIT License
727 stars 279 forks source link

enableBearerInterceptor not working #384

Open fabiopacheco18 opened 2 years ago

fabiopacheco18 commented 2 years ago

Bug Report or Feature Request (mark with an x)

- [x ] bug report -> please search for issues before submitting
- [ ] feature request

Versions.

"keycloak-angular": "9.1.0" "keycloak-js": "16.0.0" "Angular": "13.0.2"

i'm trying to implement keycloak-angular but i'm not able to add the bearer token by default to my http requests.

app.module.ts

`keycloak.init({
      config: {
        url: 'https://127.0.0.1:8443/auth',
        realm: 'dev',
        clientId: 'app',
      },
      initOptions: {
        checkLoginIframe:false,
        onLoad: 'login-required',
      },
      enableBearerInterceptor: true,
      bearerExcludedUrls:[],
      loadUserProfileAtStartUp:true,

    });`
`providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: initializeKeycloak, 
      multi: true,
      deps: [KeycloakService]
    },
  ],`
Samad-Shaikh commented 2 years ago

Hi @fabiopacheco18

You can get the bearer token by using KeycloakService.getToken(). Note that it returns Promise.

ProfEibe commented 1 year ago

I had the same problem too. I was able to get it to work after adding { provide: HTTP_INTERCEPTORS, useClass: KeycloakBearerInterceptor, multi: true }, to the providers of my app.module.ts.

I had not to do this before and I think this should happen automatically. Has something changed in angular beginning with version 13 or the last keycloak-angular updates?

KhizerRehan commented 10 months ago

Angular Version: "13.3.9", "keycloak-angular": "10.0.2", "keycloak-js": "18.0.1",

Tested and works with following packages version. Hope it can help you in case you have not found a solution:

export function initKeycloakInstance(keycloak: KeycloakService): () => Promise<boolean> {
  return (): Promise<boolean> => {
    return new Promise(async (resolve, reject) => {
      try {
        await keycloak.init({
          config: {
            url: '<KEYCLOAK_URL>',
            realm: '<REALM>',
            clientId: '<CLIENT_ID>'
          },
          initOptions: {
            onLoad: 'login-required',
            checkLoginIframe: false
          },
          enableBearerInterceptor: true, // attach ACCESS_TOKEN on each request
          bearerPrefix: 'Bearer', // prefix "bearer <TOKEN> on each request
          bearerExcludedUrls: []
        });
        resolve(true);
      } catch (error) {
        reject(error);
      }
    });
  };
}
Michae1Weiss commented 9 months ago

Hey folks,

I wanted to share a code snippet that worked well for me.

Versions. "keycloak-angular": "15.0.0" "keycloak-js": "22.0.5" "Angular": "17.0.4"

// app.config.ts

import {APP_INITIALIZER, ApplicationConfig, Provider} from '@angular/core';
import {provideRouter} from '@angular/router';

import {routes} from './app.routes';
import {KeycloakBearerInterceptor, KeycloakService} from "keycloak-angular";
import {HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi} from "@angular/common/http";

// Function to initialize Keycloak with the necessary configurations
function initializeKeycloak(keycloak: KeycloakService) {
 return () =>
   keycloak.init({
     // Configuration details for Keycloak
     config: {
       url: 'http://localhost:1234/auth', // URL of the Keycloak server
       realm: 'test', // Realm to be used in Keycloak
       clientId: 'frontend' // Client ID for the application in Keycloak
     },
     // Options for Keycloak initialization
     initOptions: {
       onLoad: 'check-sso', // Action to take on load
       silentCheckSsoRedirectUri:
         window.location.origin + '/assets/silent-check-sso.html' // URI for silent SSO checks
     },
     // Enables Bearer interceptor
     enableBearerInterceptor: true,
     // Prefix for the Bearer token
     bearerPrefix: 'Bearer',
     // URLs excluded from Bearer token addition (empty by default)
     //bearerExcludedUrls: []
   });
}

// Provider for Keycloak Bearer Interceptor
const KeycloakBearerInterceptorProvider: Provider = {
 provide: HTTP_INTERCEPTORS,
 useClass: KeycloakBearerInterceptor,
 multi: true
};

// Provider for Keycloak Initialization
const KeycloakInitializerProvider: Provider = {
 provide: APP_INITIALIZER,
 useFactory: initializeKeycloak,
 multi: true,
 deps: [KeycloakService]
}

// Exported configuration for the application
export const appConfig: ApplicationConfig = {
 providers: [
   provideHttpClient(withInterceptorsFromDi()), // Provides HttpClient with interceptors
   KeycloakInitializerProvider, // Initializes Keycloak
   KeycloakBearerInterceptorProvider, // Provides Keycloak Bearer Interceptor
   KeycloakService, // Service for Keycloak
   provideRouter(routes) // Provides routing for the application
 ]
};
KhizerRehan commented 9 months ago

@Michae1Weiss

Like prior to v17 was good enough to run following Initializer before application loads and enableBearerInterceptor: true was enough.

const KeycloakInitializerProvider: Provider = {
 provide: APP_INITIALIZER,
 useFactory: initializeKeycloak,
 multi: true,
 deps: [KeycloakService]
}

Now do we specifically need to enable todo?

// Provider for Keycloak Bearer Interceptor
const KeycloakBearerInterceptorProvider: Provider = {
 provide: HTTP_INTERCEPTORS,
 useClass: KeycloakBearerInterceptor,
 multi: true
};

As you can see my solution for v.13 https://github.com/mauriciovigolo/keycloak-angular/issues/384#issuecomment-1850331808

Mohamed-Hammada commented 8 months ago

Thanks @Michae1Weiss 👍 Same answer works with me using angular 17 and keycloak 23 I have only changed onLoad: 'login-required'

lksdesarrolloNueve commented 7 months ago

Why does it give me the following errors?

TypeError: ke.data.kernel.save.user_init is not a function kernel.js:376:37

TypeError: undefined is not a functionkernel.js:540:44

abhinaykm commented 3 weeks ago

function initializeKeycloak(keycloak: KeycloakService) { return () => keycloak.init({ config: { url: environment.keycloak.keyclockUrl, realm: environment.keycloak.realmId, clientId: environment.keycloak.clientId }, loadUserProfileAtStartUp: true, initOptions: { onLoad: 'login-required', // onLoad: 'check-sso', // flow: "standard", silentCheckSsoRedirectUri: window.location.origin + '/assets/silent-check-sso.html', checkLoginIframe: false, redirectUri: environment.keycloak.redirectUri, }, enableBearerInterceptor: true, bearerExcludedUrls: [
'/student-registration', // Exclude this path '/student-registration/*'], bearerPrefix: 'Bearer',

}).then((authenticated) => {
    if (authenticated) {
        const token = keycloak.getKeycloakInstance().token;
        if (token) {
            try {
                localStorage.setItem('token', token);
            } catch (error) {
                console.error('Error decoding token:', error);
            }
        } else {
            console.error('Access token is undefined');
        }
    } else {
        console.log('Authentication failed');
        keycloak.login();
    }
    // Subscribe to Keycloak events to handle token expiration
    keycloak.keycloakEvents$.subscribe({
        next(event) {
            if (event.type === KeycloakEventType.OnTokenExpired) {
                keycloak.updateToken(20).then((refreshed) => {
                    if (refreshed) {
                      console.log('Token was successfully refreshed');
                    } else {
                      console.warn('Token is still valid');
                    }
                  }).catch(() => {
                    console.error('Failed to refresh token, logging out');
                    keycloak.logout();
                  });
                }                
        }
    });

});

}

i wantend to exclude path but it did not work , please have any idea about that please guide me

and this routes.path export const APP_ROUTE: Route[] = [ { path: 'student-registration', redirectTo: '/student-registration/student-registration' },

{ path: '', component: MainLayoutComponent, canActivate: [AuthGuard], children: [
{ path: '', redirectTo: '/authentication/signin', pathMatch: 'full' }, }]

  auth.guard
  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 ): Promise { // List of paths to exclude from authentication const excludedPaths = ['/student-registration', '/student-registration/student-registration']; // Allow access if the current path is excluded if (excludedPaths.some(path => state.url.includes(path))) { return true; }

// If the user is not authenticated, redirect to Keycloak login
if (!this.authenticated) {
  await this.keycloak.login({
    redirectUri: window.location.origin + state.url,
  });
  return false;
}

// Check if route has required roles
const requiredRoles = (route.data as { roles: string[] }).roles;
if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) {
  return true; // Allow if no roles are required
}

// Ensure the user has all required roles
return requiredRoles.every((role) => this.roles.includes(role));

}