diff --git a/package.json b/package.json
index 063d8be87186c03b825b4a5215a9917bf716546d..543a3f1fd4ec3938052c46cc8b954aaa421a4903 100644
--- a/package.json
+++ b/package.json
@@ -26,10 +26,11 @@
     "@ngrx/router-store": "^10.0.1",
     "@ngrx/store": "^10.0.1",
     "@ngrx/store-devtools": "^10.0.1",
-    "angular-auth-oidc-client": "^11.2.2",
     "bootstrap": "^4.5.3",
     "d3": "^5.15.1",
     "jwt-decode": "^3.1.2",
+    "keycloak-angular": "^8.0.1",
+    "keycloak-js": "^11.0.3",
     "ng-inline-svg": "^11.0.1",
     "ngx-bootstrap": "^6.1.0",
     "ngx-json-viewer": "^2.4.0",
diff --git a/src/app/auth/auth.action.spec.ts b/src/app/auth/auth.action.spec.ts
deleted file mode 100644
index 1751c246acd3b93bf86d1fa8214bc4f7e32901ed..0000000000000000000000000000000000000000
--- a/src/app/auth/auth.action.spec.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import * as authActions from './auth.action';
-
-describe('[Auth] Action', () => {
-    it('should create CheckAuthAction', () => {
-        const action = new authActions.CheckAuthAction();
-        expect(action.type).toEqual(authActions.CHECK_AUTH);
-    });
-
-    it('should create CheckAuthCompleteAction', () => {
-        const action = new authActions.CheckAuthCompleteAction(true);
-        expect(action.type).toEqual(authActions.CHECK_AUTH_COMPLETE);
-        expect(action.payload).toBeTruthy();
-    });
-
-    it('should create LoginAction', () => {
-        const action = new authActions.LoginAction();
-        expect(action.type).toEqual(authActions.LOGIN);
-    });
-
-    it('should create LoginCompleteAction', () => {
-        const action = new authActions.LoginCompleteAction('toto');
-        expect(action.type).toEqual(authActions.LOGIN_COMPLETE);
-        expect(action.payload).toEqual('toto');
-    });
-
-    it('should create LogoutAction', () => {
-        const action = new authActions.LogoutAction();
-        expect(action.type).toEqual(authActions.LOGOUT);
-    });
-
-    it('should create OpenEditProfileAction', () => {
-        const action = new authActions.OpenEditProfileAction();
-        expect(action.type).toEqual(authActions.OPEN_EDIT_PROFILE);
-    });
-
-    it('should create ParseJwtAction', () => {
-        const action = new authActions.ParseJwtAction('toto');
-        expect(action.type).toEqual(authActions.PARSE_JWT);
-        expect(action.payload).toEqual('toto');
-    });
-
-    it('should create LoadUserRolesAction', () => {
-        const action = new authActions.LoadUserRolesAction(['toto']);
-        expect(action.type).toEqual(authActions.LOAD_USER_ROLES);
-        expect(action.payload).toEqual(['toto']);
-    });
-});
diff --git a/src/app/auth/auth.action.ts b/src/app/auth/auth.action.ts
index fc07c2862bce940dc66604c0b7221c9325d3899a..481b97a2ffcf383dbddec3ac8f6b70c8d762629f 100644
--- a/src/app/auth/auth.action.ts
+++ b/src/app/auth/auth.action.ts
@@ -1,117 +1,54 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
 import { Action } from '@ngrx/store';
 
-export const CHECK_AUTH = '[Auth] Check Auth';
-export const CHECK_AUTH_COMPLETE = '[Auth] Check Auth Complete';
+import { UserProfile } from './user-profile.model';
+
 export const LOGIN = '[Auth] Login';
-export const LOGIN_COMPLETE = '[Auth] Login Complete';
 export const LOGOUT = '[Auth] Logout';
+export const AUTH_SUCCESS = '[Auth] Auth Success';
+export const LOAD_USER_PROFILE_SUCCESS = '[Auth] Load User Profile Success';
+export const LOAD_USER_ROLES_SUCCESS = '[Auth] Load User Roles Success';
 export const OPEN_EDIT_PROFILE = '[Auth] Edit Profile';
-export const PARSE_JWT = '[Auth] Parse JWT';
-export const LOAD_USER_ROLES = '[Auth] Load User Roles';
-
-/**
- * @class
- * @classdesc CheckAuthAction action.
- * @readonly
- */
-export class CheckAuthAction implements Action {
-    readonly type = CHECK_AUTH;
-
-    constructor(public payload: {} = null) { }
-}
 
-/**
- * @class
- * @classdesc CheckAuthCompleteAction action.
- * @readonly
- */
-export class CheckAuthCompleteAction implements Action {
-    readonly type = CHECK_AUTH_COMPLETE;
-
-    constructor(public payload: boolean) { }
-}
-
-/**
- * @class
- * @classdesc LoginAction action.
- * @readonly
- */
 export class LoginAction implements Action {
     readonly type = LOGIN;
 
-    constructor(public payload: string = null) { }
-}
-
-/**
- * @class
- * @classdesc LoginCompleteAction action.
- * @readonly
- */
-export class LoginCompleteAction implements Action {
-    readonly type = LOGIN_COMPLETE;
-
-    constructor(public payload: any) { }
+    constructor(public payload: {} = null) { }
 }
 
-/**
- * @class
- * @classdesc LogoutAction action.
- * @readonly
- */
 export class LogoutAction implements Action {
     readonly type = LOGOUT;
 
     constructor(public payload: {} = null) { }
 }
 
-/**
- * @class
- * @classdesc OpenEditProfileAction action.
- * @readonly
- */
-export class OpenEditProfileAction implements Action {
-    readonly type = OPEN_EDIT_PROFILE;
+export class AuthSuccessAction implements Action {
+    readonly type = AUTH_SUCCESS;
 
     constructor(public payload: {} = null) { }
 }
 
-/**
- * @class
- * @classdesc ParseJwtAction action.
- * @readonly
- */
-export class ParseJwtAction implements Action {
-    readonly type = PARSE_JWT;
+export class LoadUserProfileSuccessAction implements Action {
+    readonly type = LOAD_USER_PROFILE_SUCCESS;
 
-    constructor(public payload: string) { }
+    constructor(public payload: UserProfile) { }
 }
 
-/**
- * @class
- * @classdesc LoadUserRolesAction action.
- * @readonly
- */
-export class LoadUserRolesAction implements Action {
-    readonly type = LOAD_USER_ROLES;
+export class LoadUserRolesSuccessAction implements Action {
+    readonly type = LOAD_USER_ROLES_SUCCESS;
 
     constructor(public payload: string[]) { }
 }
 
+export class OpenEditProfileAction implements Action {
+    readonly type = OPEN_EDIT_PROFILE;
+
+    constructor(public payload: {} = null) { }
+}
+
 export type Actions
-    = CheckAuthAction
-    | CheckAuthCompleteAction
-    | LoginAction
-    | LoginCompleteAction
+    = LoginAction
     | LogoutAction
-    | OpenEditProfileAction
-    | ParseJwtAction
-    | LoadUserRolesAction;
+    | AuthSuccessAction
+    | LoadUserProfileSuccessAction
+    | LoadUserRolesSuccessAction
+    | OpenEditProfileAction;
diff --git a/src/app/auth/auth.effects.ts b/src/app/auth/auth.effects.ts
index b84de91045583ae069de33451992b314eac4b149..7f492302140914a38199cc0cb3a47375e3560357 100644
--- a/src/app/auth/auth.effects.ts
+++ b/src/app/auth/auth.effects.ts
@@ -1,134 +1,48 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
 import { Injectable } from '@angular/core';
-import { Router } from '@angular/router';
+import { from } from 'rxjs';
+import { switchMap, tap } from 'rxjs/operators';
 
 import { Effect, Actions, ofType } from '@ngrx/effects';
-import { of } from 'rxjs';
-import { switchMap, map, tap } from 'rxjs/operators';
-import jwt_decode from 'jwt-decode';
+import { KeycloakService } from 'keycloak-angular';
 
 import * as authActions from './auth.action';
-import { AuthService } from './auth.service';
 import { environment } from '../../environments/environment';
 
 @Injectable()
-/**
- * @class
- * @classdesc Authentication effects.
- */
 export class AuthEffects {
     constructor(
         private actions$: Actions,
-        private authService: AuthService,
-        private router: Router
+        private keycloak: KeycloakService
     ) { }
 
-    /**
-     * Executes log in (OIDC Login).
-     */
     @Effect({ dispatch: false })
     loginAction$ = this.actions$.pipe(
         ofType(authActions.LOGIN),
-        tap((action: authActions.LoginAction) => {
-            const redirectUrl = action.payload;
-            if (redirectUrl !== null) {
-                sessionStorage.setItem(environment.ssoClientId + '_redirectUrl', redirectUrl);
-            }
-            return this.authService.doLogin()
-        })
-    );
-
-    /**
-     * Checks if user is authenticated.
-     */
-    @Effect()
-    checkAuthAction$ = this.actions$.pipe(
-        ofType(authActions.CHECK_AUTH),
-        switchMap(() =>
-            this.authService
-                .checkAuth()
-                .pipe(
-                    map((isAuthenticated) => new authActions.CheckAuthCompleteAction(isAuthenticated))
-                )
-        )
-    );
-
-    /**
-     * Completes authentication if not complete.
-     */
-    @Effect()
-    checkAuthCompleteAction$ = this.actions$.pipe(
-        ofType(authActions.CHECK_AUTH_COMPLETE),
-        switchMap((action: authActions.CheckAuthCompleteAction) => {
-            const isAuthenticated = action.payload;
-
-            if (isAuthenticated) {
-                return this.authService.userData.pipe(
-                    switchMap((profile) => [
-                        new authActions.LoginCompleteAction(profile),
-                        new authActions.ParseJwtAction(this.authService.token)
-                    ])
-                );
-            } else {
-                return of({ type: '[No Action] ' + authActions.CHECK_AUTH_COMPLETE });
-            }
-        })
+        tap(_ => this.keycloak.login())
     );
 
-    /**
-     * Redirects user after login completes.
-     */
     @Effect({ dispatch: false })
-    loginCompleteAction$ = this.actions$.pipe(
-        ofType(authActions.LOGIN_COMPLETE),
-        tap(_ => {
-            const redirectUrl = sessionStorage.getItem(environment.ssoClientId + '_redirectUrl');
-            if (redirectUrl !== null) {
-                sessionStorage.removeItem(environment.ssoClientId + '_redirectUrl');
-                this.router.navigateByUrl(redirectUrl);
-            }
-        })
+    logoutAction$ = this.actions$.pipe(
+        ofType(authActions.LOGOUT),
+        tap(_ => this.keycloak.logout())
     );
 
-    /**
-     * Parses token to get user roles.
-     */
     @Effect()
-    parseJWTAction$ = this.actions$.pipe(
-        ofType(authActions.PARSE_JWT),
-        switchMap((action: authActions.ParseJwtAction) => {
-            const jwt: any = jwt_decode(action.payload);
-            if (environment.ssoName === 'auth0') {
-                return of({ type: '[No Action] ' + authActions.PARSE_JWT });
-            } else {
-                return of(new authActions.LoadUserRolesAction(jwt.realm_access.roles));
-            }
-        })
-    );
-
-    /**
-     * Executes log out.
-     */
-    @Effect({ dispatch: false })
-    logoutAction$ = this.actions$.pipe(
-        ofType(authActions.LOGOUT),
-        tap(_ => this.authService.signOut())
+    authSuccessAction$ = this.actions$.pipe(
+        ofType(authActions.AUTH_SUCCESS),
+        switchMap(_ =>
+            from(this.keycloak.loadUserProfile()).pipe(
+                switchMap(userProfile => [
+                    new authActions.LoadUserProfileSuccessAction(userProfile),
+                    new authActions.LoadUserRolesSuccessAction(this.keycloak.getUserRoles())
+                ])
+            )
+        )
     );
 
-    /**
-     * Opens edit profile page.
-     */
     @Effect({ dispatch: false })
-    openEditProfileAction$ = this.actions$.pipe(
+    OpenEditProfileAction$ = this.actions$.pipe(
         ofType(authActions.OPEN_EDIT_PROFILE),
-        tap(_ => window.open(environment.ssoAuthUrl + '/account', '_blank'))
+        tap(_ => window.open(environment.ssoAuthUrl + '/realms/' + environment.ssoRealm + '/account', '_blank'))
     );
 }
diff --git a/src/app/auth/auth.module.ts b/src/app/auth/auth.module.ts
index 67188544aca69fccb0b473fd0fef82add26b7b13..8d7d58afc16229fe4ca6add1af004262bf99dd60 100644
--- a/src/app/auth/auth.module.ts
+++ b/src/app/auth/auth.module.ts
@@ -1,49 +1,21 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-import { NgModule, APP_INITIALIZER } from '@angular/core';
-import { HTTP_INTERCEPTORS } from '@angular/common/http';
+import { NgModule } from '@angular/core';
 
+import { KeycloakAngularModule } from 'keycloak-angular';
 import { StoreModule } from '@ngrx/store';
 import { EffectsModule } from '@ngrx/effects';
-import { AuthModule as AuthOIDCModule, OidcConfigService } from 'angular-auth-oidc-client';
 
+import { initializeKeycloakAnis } from './init.keycloak';
 import { reducer } from './auth.reducer';
 import { AuthEffects } from './auth.effects';
-import { AuthService } from './auth.service';
-import { configureAuth } from './config-auth';
-import { TokenInterceptor } from './token-interceptor';
 
 @NgModule({
     imports: [
-        AuthOIDCModule.forRoot(),
+        KeycloakAngularModule,
         StoreModule.forFeature('auth', reducer),
         EffectsModule.forFeature([ AuthEffects ])
     ],
     providers: [
-        OidcConfigService,
-        {
-            provide: APP_INITIALIZER,
-            useFactory: configureAuth,
-            deps: [OidcConfigService],
-            multi: true,
-        },
-        AuthService,
-        {
-            provide: HTTP_INTERCEPTORS,
-            useClass: TokenInterceptor,
-            multi: true
-        }
+        initializeKeycloakAnis
     ]
 })
-/**
- * @class
- * @classdesc Authentication module.
- */
 export class AuthModule { }
diff --git a/src/app/auth/auth.reducer.spec.ts b/src/app/auth/auth.reducer.spec.ts
deleted file mode 100644
index 4cf2dae8e0f45ecbfa6bc049275ba777d7ad1681..0000000000000000000000000000000000000000
--- a/src/app/auth/auth.reducer.spec.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import * as fromAuth from './auth.reducer';
-import * as authActions from './auth.action';
-
-describe('[Auth] Reducer', () => {
-    it('should return init state', () => {
-        const { initialState } = fromAuth;
-        const action = {} as authActions.Actions;
-        const state = fromAuth.reducer(undefined, action);
-
-        expect(state).toBe(initialState);
-    });
-
-    it('should set isAuthenticated to true and set userProfile', () => {
-        const { initialState } = fromAuth;
-        const action = new authActions.LoginCompleteAction('toto');
-        const state = fromAuth.reducer(initialState, action);
-
-        expect(state.isAuthenticated).toBeTruthy();
-        expect(state.userProfile).toEqual('toto');
-        expect(state.userRoles.length).toEqual(0);
-        expect(state).not.toEqual(initialState);
-    });
-
-    it('should set userRoles', () => {
-        const { initialState } = fromAuth;
-        const action = new authActions.LoadUserRolesAction(['toto']);
-        const state = fromAuth.reducer(initialState, action);
-
-        expect(state.isAuthenticated).toBeFalsy();
-        expect(state.userProfile).toBeNull();
-        expect(state.userRoles.length).toEqual(1);
-        expect(state.userRoles[0]).toEqual('toto');
-        expect(state).not.toEqual(initialState);
-    });
-
-    it('should set isAuthenticated to false and unset userRoles', () => {
-        const initialState = { ...fromAuth.initialState, isAuthenticated: true, userProfile: 'toto' };
-        const action = new authActions.LogoutAction();
-        const state = fromAuth.reducer(initialState, action);
-
-        expect(state.isAuthenticated).toBeFalsy();
-        expect(state.userProfile).toBeNull();
-        expect(state.userRoles.length).toEqual(0);
-        expect(state).not.toEqual(initialState);
-    });
-
-    it('should get isAuthenticated', () => {
-        const action = {} as authActions.Actions;
-        const state = fromAuth.reducer(undefined, action);
-
-        expect(fromAuth.isAuthenticated(state)).toBeFalsy();
-    });
-
-    it('should get userProfile', () => {
-        const action = {} as authActions.Actions;
-        const state = fromAuth.reducer(undefined, action);
-
-        expect(fromAuth.getUserProfile(state)).toBeNull();
-    });
-
-    it('should get userRoles', () => {
-        const action = {} as authActions.Actions;
-        const state = fromAuth.reducer(undefined, action);
-
-        expect(fromAuth.getUserRoles(state).length).toEqual(0);
-    });
-});
diff --git a/src/app/auth/auth.reducer.ts b/src/app/auth/auth.reducer.ts
index 87d6637a09f2bf173732e78ea547ed3ef7da8d00..576ca0100cf210e755ca825a5195a957a58617c1 100644
--- a/src/app/auth/auth.reducer.ts
+++ b/src/app/auth/auth.reducer.ts
@@ -1,22 +1,10 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
 import * as actions from './auth.action';
 
-/**
- * Interface for authentication state.
- *
- * @interface State
- */
+import { UserProfile } from './user-profile.model';
+
 export interface State {
     isAuthenticated: boolean;
-    userProfile: any;
+    userProfile: UserProfile;
     userRoles: string[];
 }
 
@@ -26,35 +14,29 @@ export const initialState: State = {
     userRoles: []
 };
 
-/**
- * Reduces state.
- *
- * @param  {State} state - The state.
- * @param  {actions} action - The action.
- *
- * @return State
- */
 export function reducer(state: State = initialState, action: actions.Actions): State {
     switch (action.type) {
-        case actions.LOGIN_COMPLETE:
+        case actions.AUTH_SUCCESS:
             return {
                 ...state,
-                isAuthenticated: true,
-                userProfile: action.payload
+                isAuthenticated: true
             };
 
-        case actions.LOAD_USER_ROLES:
+        case actions.LOAD_USER_PROFILE_SUCCESS:
+            const userProfile = action.payload;
+
             return {
                 ...state,
-                userRoles: action.payload
-            };    
+                userProfile
+            };
+
+        case actions.LOAD_USER_ROLES_SUCCESS:
+            const userRoles = action.payload;
 
-        case actions.LOGOUT:
             return {
                 ...state,
-                isAuthenticated: false,
-                userProfile: null
-            }
+                userRoles
+            };
 
         default:
             return state;
diff --git a/src/app/auth/auth.selector.spec.ts b/src/app/auth/auth.selector.spec.ts
deleted file mode 100644
index 64a4e089015f4ce1b61f70ef8b0ac4389565de2e..0000000000000000000000000000000000000000
--- a/src/app/auth/auth.selector.spec.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as authSelector from './auth.selector';
-import * as fromAuth from './auth.reducer';
-
-describe('[Auth] Selector', () => {
-    it('should get isAuthenticated', () => {
-        const state = { auth: { ...fromAuth.initialState }};
-        expect(authSelector.isAuthenticated(state)).toBeFalsy();
-    });
-
-    it('should get isAuthenticated', () => {
-        const state = { auth: { ...fromAuth.initialState }};
-        expect(authSelector.isAuthenticated(state)).toBeFalsy();
-    });
-
-    it('should get userProfile', () => {
-        const state = { auth: { ...fromAuth.initialState }};
-        expect(authSelector.getUserProfile(state)).toBeNull();
-    });
-
-    it('should get userRoles', () => {
-        const state = { auth: { ...fromAuth.initialState }};
-        expect(authSelector.getUserRoles(state).length).toEqual(0);
-    });
-});
\ No newline at end of file
diff --git a/src/app/auth/auth.selector.ts b/src/app/auth/auth.selector.ts
index db6ccddec34494926d889d520eecaa85a3918e54..a58dfd67eb2ee9295cbcd18d4df0aa76ac438733 100644
--- a/src/app/auth/auth.selector.ts
+++ b/src/app/auth/auth.selector.ts
@@ -1,12 +1,3 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
 import { createSelector, createFeatureSelector } from '@ngrx/store';
 
 import * as auth from './auth.reducer';
diff --git a/src/app/auth/auth.service.ts b/src/app/auth/auth.service.ts
deleted file mode 100644
index d7f8c45a595fceb7fbb612565620203db0614e68..0000000000000000000000000000000000000000
--- a/src/app/auth/auth.service.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-import { Injectable } from '@angular/core';
-
-import { Observable, of } from 'rxjs';
-import { OidcSecurityService } from 'angular-auth-oidc-client';
-
-import { environment } from '../../environments/environment';
-
-@Injectable({ providedIn: 'root' })
-/**
- * @class
- * @classdesc Authentication service.
- */
-export class AuthService {
-    constructor(private oidcSecurityService: OidcSecurityService) { }
-
-    /**
-     * Gets user data.
-     *
-     * @return Observable<any>
-     */
-    get token() {
-        if (environment.ssoName === 'auth0') {
-            return this.oidcSecurityService.getIdToken();
-        } else {
-            return this.oidcSecurityService.getToken();
-        }
-    }
-
-    /**
-     * Gets user data.
-     *
-     * @return Observable<any>
-     */
-    get userData(): Observable<any> {
-        return this.oidcSecurityService.userData$;
-    }
-
-    /**
-     * Checks authentication.
-     *
-     * @return Observable<boolean>
-     */
-    checkAuth(): Observable<boolean> {
-        return this.oidcSecurityService.checkAuthIncludingServer();
-    }
-
-    /**
-     * Logs in user.
-     */
-    doLogin() {
-        this.oidcSecurityService.authorize();
-    }
-
-    /**
-     * Logs out user.
-     */
-    signOut(): void {
-        this.oidcSecurityService.logoffAndRevokeTokens();
-    }
-}
diff --git a/src/app/auth/config-auth.ts b/src/app/auth/config-auth.ts
deleted file mode 100644
index 309326bb1362692e753129dcec23d97c187e3ffe..0000000000000000000000000000000000000000
--- a/src/app/auth/config-auth.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-import { OidcConfigService, LogLevel } from 'angular-auth-oidc-client';
-
-import { environment } from '../../environments/environment';
-
-/**
- * Adds user token to the request.
- *
- * @param  {OidcConfigService} oidcConfigService - The single sign on service.
- *
- * @return any
- */
-export function configureAuth(oidcConfigService: OidcConfigService): any {
-    return () => oidcConfigService.withConfig({
-        stsServer: environment.ssoAuthUrl,
-        redirectUrl: window.location.origin,
-        postLogoutRedirectUri: window.location.origin,
-        clientId: environment.ssoClientId,
-        scope: 'openid profile email offline_access',
-        responseType: 'code',
-        silentRenew: true,
-        silentRenewUrl: `${window.location.origin}/silent-renew.html`,
-        renewTimeBeforeTokenExpiresInSeconds: 10,
-        logLevel: LogLevel.None,
-    });
-}
diff --git a/src/app/auth/init.keycloak.ts b/src/app/auth/init.keycloak.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3b9e98db17f3c6381b1aeabece40054d1196fe5f
--- /dev/null
+++ b/src/app/auth/init.keycloak.ts
@@ -0,0 +1,40 @@
+import { APP_INITIALIZER } from '@angular/core';
+import { from } from 'rxjs';
+
+import { KeycloakService, KeycloakEventType } from 'keycloak-angular';
+import { Store } from '@ngrx/store';
+
+import * as keycloakActions from './auth.action';
+import * as fromKeycloak from './auth.reducer';
+import { environment } from '../../environments/environment';
+
+function initializeKeycloak(keycloak: KeycloakService, store: Store<{ keycloak: fromKeycloak.State }>) {
+    return async () => {
+        from(keycloak.keycloakEvents$).subscribe(event => {
+            if (event.type === KeycloakEventType.OnAuthSuccess) {
+                store.dispatch(new keycloakActions.AuthSuccessAction());
+            }
+        })
+
+        return keycloak.init({
+            config: {
+                url: environment.ssoAuthUrl,
+                realm: environment.ssoRealm,
+                clientId: environment.ssoClientId,
+            },
+            initOptions: {
+                onLoad: 'check-sso',
+                silentCheckSsoRedirectUri:
+                    window.location.origin + '/assets/silent-check-sso.html'
+            },
+            loadUserProfileAtStartUp: true
+        });
+    }
+}
+
+export const initializeKeycloakAnis = {
+    provide: APP_INITIALIZER,
+    useFactory: initializeKeycloak,
+    multi: true,
+    deps: [ KeycloakService, Store ],
+};
diff --git a/src/app/auth/token-interceptor.ts b/src/app/auth/token-interceptor.ts
deleted file mode 100644
index d54c36f7e7e1ce25a62dfd68a7d13645ef2a4b14..0000000000000000000000000000000000000000
--- a/src/app/auth/token-interceptor.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-import { Injectable } from '@angular/core';
-import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
-
-import { Observable } from 'rxjs';
-
-import { AuthService } from './auth.service';
-import { environment } from '../../environments/environment';
-
-@Injectable()
-/**
- * @class
- * @classdesc Token interceptor.
- *
- * @implements HttpInterceptor
- */
-export class TokenInterceptor implements HttpInterceptor {
-    constructor(private authService: AuthService) { }
-
-    /**
-     * Adds user token to the request.
-     *
-     * @param  {HttpRequest<any>} request - The outgoing request.
-     * @param  {HttpHandler} next - The next interceptor.
-     *
-     * @return Observable<HttpEvent<any>>
-     */
-    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
-        let requestToForward = request;
-        const token = this.authService.token;
-        const isApiUrl = request.url.startsWith(environment.apiUrl);
-        if (token !== '' && isApiUrl) {
-            requestToForward = request.clone({
-                setHeaders: {
-                    Authorization: `Bearer ${token}`
-                }
-            });
-        }
-        return next.handle(requestToForward);
-    }
-}
diff --git a/src/app/auth/user-profile.model.ts b/src/app/auth/user-profile.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2addf7e0613e83e9015c843bef7ad7131352113
--- /dev/null
+++ b/src/app/auth/user-profile.model.ts
@@ -0,0 +1,11 @@
+export interface UserProfile {
+    id?: string;
+    username?: string;
+    email?: string;
+    firstName?: string;
+    lastName?: string;
+    enabled?: boolean;
+    emailVerified?: boolean;
+    totp?: boolean;
+    createdTimestamp?: number;
+}
diff --git a/src/app/core/components/nav.component.html b/src/app/core/components/nav.component.html
index 5d0a999de620bb71e477db72cfd7424e8aecfc64..8161ec8052c17293c5444745d43fbae0cd7b8296 100644
--- a/src/app/core/components/nav.component.html
+++ b/src/app/core/components/nav.component.html
@@ -49,12 +49,12 @@
                     <span class="dropdown-item font-italic">{{ userProfile.email }}</span>
                 </li>
                 <li class="divider dropdown-divider"></li>
-                <li *ngIf="ssoName != 'auth0'" role="menuitem">
+                <li role="menuitem">
                     <button class="dropdown-item pointer" (click)="openEditProfile.emit()">
                         <span class="fas fa-id-card"></span> Edit profile
                     </button>
                 </li>
-                <li *ngIf="ssoName != 'auth0'" class="divider dropdown-divider"></li>
+                <li class="divider dropdown-divider"></li>
                 <li role="menuitem">
                     <button class="dropdown-item text-danger pointer" (click)="logout.emit()">
                         <span class="fas fa-sign-out-alt fa-fw"></span> Sign Out
@@ -96,12 +96,12 @@
                 </a>
             </li>
             <li *ngIf="getConfig('authentication', 'allowed') && isAuthenticated" class="divider dropdown-divider"></li>
-            <li *ngIf="getConfig('authentication', 'allowed') && isAuthenticated && ssoName != 'auth0'" role="menuitem">
+            <li *ngIf="getConfig('authentication', 'allowed') && isAuthenticated" role="menuitem">
                 <button class="dropdown-item pointer" (click)="openEditProfile.emit()">
                     <span class="fas fa-id-card"></span> Edit profile
                 </button>
             </li>
-            <li *ngIf="getConfig('authentication', 'allowed') && ssoName != 'auth0'" class="divider dropdown-divider"></li>
+            <li *ngIf="getConfig('authentication', 'allowed')" class="divider dropdown-divider"></li>
             <li role="menuitem">
                 <button *ngIf="getConfig('authentication', 'allowed') && !isAuthenticated"
                     class="dropdown-item text-success"
diff --git a/src/app/core/components/nav.component.ts b/src/app/core/components/nav.component.ts
index e65122ab9c8a423931bbcc069e0abbdd91e654bb..a714d2ef5801aef1df3e77f247c762c062a84c43 100644
--- a/src/app/core/components/nav.component.ts
+++ b/src/app/core/components/nav.component.ts
@@ -10,6 +10,7 @@
 import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
 
 import { Instance } from '../../metamodel/model';
+import { UserProfile } from '../../auth/user-profile.model';
 import { environment } from '../../../environments/environment'
 
 @Component({
@@ -24,14 +25,13 @@ import { environment } from '../../../environments/environment'
  */
 export class NavComponent {
     @Input() isAuthenticated: boolean;
-    @Input() userProfile: any;
+    @Input() userProfile: UserProfile;
     @Input() instance: Instance;
     @Output() login: EventEmitter<any> = new EventEmitter();
     @Output() logout: EventEmitter<any> = new EventEmitter();
     @Output() openEditProfile: EventEmitter<any> = new EventEmitter();
 
     baseHref: string = environment.baseHref;
-    ssoName: string = environment.ssoName;
 
     /**
      * Checks if given key for the given configuration propriety is allowed.
diff --git a/src/app/core/containers/app.component.spec.ts b/src/app/core/containers/app.component.spec.ts
index a53e5eb3b1e31e38f11efc784104b5eecdb097c9..de13a0b9d58ab26776fa3d16ae7c748f25aeeb0b 100644
--- a/src/app/core/containers/app.component.spec.ts
+++ b/src/app/core/containers/app.component.spec.ts
@@ -46,16 +46,14 @@ describe('[Core] Container: AppComponent', () => {
 
     it('should execute ngOnInit lifecycle', () => {
         const loadInstanceMetaAction = new instanceActions.LoadInstanceMetaAction();
-        const checkAuthAction = new authActions.CheckAuthAction();
         const spy = spyOn(store, 'dispatch');
         component.ngOnInit();
-        expect(spy).toHaveBeenCalledTimes(2);
+        expect(spy).toHaveBeenCalledTimes(1);
         expect(spy).toHaveBeenCalledWith(loadInstanceMetaAction);
-        expect(spy).toHaveBeenCalledWith(checkAuthAction);
     });
 
     it('#login() should dispatch LoginAction', () => {
-        const loginAction = new authActions.LoginAction('/');
+        const loginAction = new authActions.LoginAction();
         const spy = spyOn(store, 'dispatch');
         component.login();
         expect(spy).toHaveBeenCalledTimes(1);
diff --git a/src/app/core/containers/app.component.ts b/src/app/core/containers/app.component.ts
index e556cb08353fff9caca9189832926ffdfd712f03..ef662f5a0b9752132646e35555e20d37be40fbe7 100644
--- a/src/app/core/containers/app.component.ts
+++ b/src/app/core/containers/app.component.ts
@@ -8,7 +8,6 @@
  */
 
 import { Component, ViewEncapsulation, OnInit } from '@angular/core';
-import { Router } from '@angular/router';
 
 import { Store } from '@ngrx/store';
 import { Observable } from 'rxjs';
@@ -19,6 +18,7 @@ import * as metamodelSelector from '../../metamodel/selectors';
 import * as fromAuth from '../../auth/auth.reducer';
 import * as authActions from '../../auth/auth.action';
 import * as authSelector from '../../auth/auth.selector';
+import { UserProfile } from '../../auth/user-profile.model';
 import { Instance } from '../../metamodel/model';
 import { VERSIONS } from '../../../settings/settings';
 
@@ -48,10 +48,10 @@ export class AppComponent implements OnInit {
     public anisClientVersion: string = VERSIONS.anisClient;
     public year = (new Date()).getFullYear();
     public isAuthenticated: Observable<boolean>;
-    public userProfile: Observable<any>;
+    public userProfile: Observable<UserProfile>;
     public instance: Observable<Instance>;
 
-    constructor(private store: Store<StoreState>, private router: Router) {
+    constructor(private store: Store<StoreState>) {
         this.isAuthenticated = store.select(authSelector.isAuthenticated);
         this.userProfile = store.select(authSelector.getUserProfile);
         this.instance = store.select(metamodelSelector.getInstance);
@@ -59,14 +59,13 @@ export class AppComponent implements OnInit {
 
     ngOnInit() {
         this.store.dispatch(new instanceActions.LoadInstanceMetaAction());
-        this.store.dispatch(new authActions.CheckAuthAction());
     }
 
     /**
      * Dispatches action to log in.
      */
     login(): void {
-        this.store.dispatch(new authActions.LoginAction(this.router.url));
+        this.store.dispatch(new authActions.LoginAction());
     }
 
     /**
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index bcf0250547b4046a4c50fd0a5993e816805efc51..c3fdee522f6b8f094acd22f39e1c4a5f92ce3fe3 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -14,7 +14,7 @@ export const environment = {
     spectraUrl: 'https://cesam.lam.fr/anis-tools/spectra_to_csv/?file=/dataproject/SPECTRA/',
     instanceName: 'default',
     baseHref: '/',
-    ssoName: 'keycloak',
-    ssoAuthUrl: 'https://anis-dev.lam.fr/auth/realms/anis',
+    ssoAuthUrl: 'https://anis-dev.lam.fr/auth',
+    ssoRealm: 'anis',
     ssoClientId: 'anis-client'
 };
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index a92b10b39a42a48ce382e81358e25c9307bdd6c7..239025093d1bcbfab536bddbba8e3ef5cd674e4c 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -18,7 +18,7 @@ export const environment = {
     spectraUrl: 'https://cesam.lam.fr/anis-tools/spectra_to_csv/?file=/dataproject/SPECTRA/',
     instanceName: 'default',
     baseHref: '/',
-    ssoName: 'keycloak',
-    ssoAuthUrl: 'http://localhost:8180/auth/realms/anis',
+    ssoAuthUrl: 'http://localhost:8180/auth',
+    ssoRealm: 'anis',
     ssoClientId: 'anis-client'
 };
diff --git a/yarn.lock b/yarn.lock
index 9d7951e3c1f80cc83990fad1ea84a11e7db02aa5..d34144c342012542c2b90ac111a9e2ee9b511660 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2014,14 +2014,6 @@ alphanum-sort@^1.0.0:
   resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
   integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
 
-angular-auth-oidc-client@^11.2.2:
-  version "11.2.2"
-  resolved "https://registry.yarnpkg.com/angular-auth-oidc-client/-/angular-auth-oidc-client-11.2.2.tgz#59c3875042478e64736cb286cb7095b8126d1cf1"
-  integrity sha512-eBt6iuEF2mC2AL3IINXXrb0zKGPeJ8LXrYav4cnum6vNr0b8XbD1guFtu+/j699qge4rPDWoncPJzYk/VuZ0LA==
-  dependencies:
-    common-tags "^1.8.0"
-    jsrsasign-reduced "^8.0.15"
-
 ansi-colors@4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
@@ -2339,6 +2331,11 @@ base64-arraybuffer@0.1.5:
   resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
   integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
 
+base64-js@1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
+  integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+
 base64-js@^1.0.2:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
@@ -3050,11 +3047,6 @@ commander@^2.11.0, commander@^2.12.1:
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
   integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
 
-common-tags@^1.8.0:
-  version "1.8.0"
-  resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
-  integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==
-
 commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -5846,6 +5838,11 @@ jest-worker@^26.3.0:
     merge-stream "^2.0.0"
     supports-color "^7.0.0"
 
+js-sha256@0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
+  integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -5947,11 +5944,6 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.10.0"
 
-jsrsasign-reduced@^8.0.15:
-  version "8.0.15"
-  resolved "https://registry.yarnpkg.com/jsrsasign-reduced/-/jsrsasign-reduced-8.0.15.tgz#a1b110cdbb83bc0af91e9c66bee6f62ce0c9719d"
-  integrity sha512-Ig4W69nXCIUedzOSk3nqJWUr2DmSDENYfsmCqVK33GPETtPcjwREGQc92hV5jcJ6zavMvGD4tjhZ+T7JIiaSLA==
-
 jszip@^3.1.3:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.2.1.tgz#c5d32df7274042282b157efb16e522b43435e01a"
@@ -6041,6 +6033,21 @@ karma@~5.0.0:
     ua-parser-js "0.7.21"
     yargs "^15.3.1"
 
+keycloak-angular@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/keycloak-angular/-/keycloak-angular-8.0.1.tgz#29851e7aded21925faa051c69dfa5872bda6661f"
+  integrity sha512-q68vcaFiSYNjbzPM1v+6LohMpWUyus9hcQBi2rNz06xOtWuRU4U6t5vQgoim6bDhtkhWpR5+a3SYl0lzUJKyrw==
+  dependencies:
+    tslib "^2.0.0"
+
+keycloak-js@^11.0.3:
+  version "11.0.3"
+  resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-11.0.3.tgz#5f22f22662211e2bfa5327d3d2eb83020a5baa23"
+  integrity sha512-e2OVyCiru25UhJz3aPj5irf//+vJzvAhHdcsCIWAcvF8Te22iUoZqEdNFji8D3zNzDehX4VpuIJwQOYCj6rqTA==
+  dependencies:
+    base64-js "1.3.1"
+    js-sha256 "0.9.0"
+
 killable@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"