mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
Added check for external auth and page refresh
This commit is contained in:

committed by
Michael W Spalti

parent
ade9533f4c
commit
290a89909e
@@ -17,6 +17,7 @@ export const AuthActionTypes = {
|
||||
AUTHENTICATED_SUCCESS: type('dspace/auth/AUTHENTICATED_SUCCESS'),
|
||||
CHECK_AUTHENTICATION_TOKEN: type('dspace/auth/CHECK_AUTHENTICATION_TOKEN'),
|
||||
CHECK_AUTHENTICATION_TOKEN_COOKIE: type('dspace/auth/CHECK_AUTHENTICATION_TOKEN_COOKIE'),
|
||||
SET_AUTH_COOKIE_STATUS: type('dspace/auth/SET_AUTH_COOKIE_STATUS'),
|
||||
RETRIEVE_AUTH_METHODS: type('dspace/auth/RETRIEVE_AUTH_METHODS'),
|
||||
RETRIEVE_AUTH_METHODS_SUCCESS: type('dspace/auth/RETRIEVE_AUTH_METHODS_SUCCESS'),
|
||||
RETRIEVE_AUTH_METHODS_ERROR: type('dspace/auth/RETRIEVE_AUTH_METHODS_ERROR'),
|
||||
@@ -150,6 +151,19 @@ export class CheckAuthenticationTokenCookieAction implements Action {
|
||||
public type: string = AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication cookie status to flag an external authentication response.
|
||||
*/
|
||||
export class SetAuthCookieStatus implements Action {
|
||||
public type: string = AuthActionTypes.SET_AUTH_COOKIE_STATUS;
|
||||
|
||||
payload = false;
|
||||
|
||||
constructor(exists: boolean) {
|
||||
this.payload = exists;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign out.
|
||||
* @class LogOutAction
|
||||
@@ -425,6 +439,7 @@ export type AuthActions
|
||||
| AuthenticationSuccessAction
|
||||
| CheckAuthenticationTokenAction
|
||||
| CheckAuthenticationTokenCookieAction
|
||||
| SetAuthCookieStatus
|
||||
| RedirectWhenAuthenticationIsRequiredAction
|
||||
| RedirectWhenTokenExpiredAction
|
||||
| AddAuthenticationMessageAction
|
||||
|
@@ -214,12 +214,14 @@ describe('AuthEffects', () => {
|
||||
authenticated: true
|
||||
})
|
||||
);
|
||||
spyOn((authEffects as any).authService, 'setExternalAuthStatus');
|
||||
actions = hot('--a-', { a: { type: AuthActionTypes.CHECK_AUTHENTICATION_TOKEN_COOKIE } });
|
||||
|
||||
const expected = cold('--b-', { b: new RetrieveTokenAction() });
|
||||
|
||||
expect(authEffects.checkTokenCookie$).toBeObservable(expected);
|
||||
authEffects.checkTokenCookie$.subscribe(() => {
|
||||
expect(authServiceStub.setExternalAuthStatus).toHaveBeenCalledWith(true);
|
||||
expect((authEffects as any).authorizationsService.invalidateAuthorizationsRequestCache).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@@ -153,6 +153,7 @@ export class AuthEffects {
|
||||
return this.authService.checkAuthenticationCookie().pipe(
|
||||
map((response: AuthStatus) => {
|
||||
if (response.authenticated) {
|
||||
this.authService.setExternalAuthStatus(true);
|
||||
this.authorizationsService.invalidateAuthorizationsRequestCache();
|
||||
return new RetrieveTokenAction();
|
||||
} else {
|
||||
|
@@ -8,6 +8,7 @@ import {
|
||||
AuthenticationErrorAction,
|
||||
AuthenticationSuccessAction,
|
||||
CheckAuthenticationTokenAction,
|
||||
SetAuthCookieStatus,
|
||||
CheckAuthenticationTokenCookieAction,
|
||||
LogOutAction,
|
||||
LogOutErrorAction,
|
||||
@@ -219,6 +220,28 @@ describe('authReducer', () => {
|
||||
expect(newState).toEqual(state);
|
||||
});
|
||||
|
||||
it('should set the authentication cookie status in response to a SET_AUTH_COOKIE_STATUS action', () => {
|
||||
initialState = {
|
||||
authenticated: true,
|
||||
loaded: false,
|
||||
blocking: false,
|
||||
loading: true,
|
||||
externalAuth: false,
|
||||
idle: false
|
||||
};
|
||||
const action = new SetAuthCookieStatus(true);
|
||||
const newState = authReducer(initialState, action);
|
||||
state = {
|
||||
authenticated: true,
|
||||
loaded: false,
|
||||
blocking: false,
|
||||
loading: true,
|
||||
externalAuth: true,
|
||||
idle: false
|
||||
};
|
||||
expect(newState).toEqual(state);
|
||||
});
|
||||
|
||||
it('should properly set the state, in response to a LOG_OUT action', () => {
|
||||
initialState = {
|
||||
authenticated: true,
|
||||
|
@@ -10,7 +10,7 @@ import {
|
||||
RedirectWhenTokenExpiredAction,
|
||||
RefreshTokenSuccessAction,
|
||||
RetrieveAuthenticatedEpersonSuccessAction,
|
||||
RetrieveAuthMethodsSuccessAction,
|
||||
RetrieveAuthMethodsSuccessAction, SetAuthCookieStatus,
|
||||
SetRedirectUrlAction
|
||||
} from './auth.actions';
|
||||
// import models
|
||||
@@ -59,6 +59,8 @@ export interface AuthState {
|
||||
// all authentication Methods enabled at the backend
|
||||
authMethods?: AuthMethod[];
|
||||
|
||||
externalAuth?: boolean,
|
||||
|
||||
// true when the current user is idle
|
||||
idle: boolean;
|
||||
|
||||
@@ -73,6 +75,7 @@ const initialState: AuthState = {
|
||||
blocking: true,
|
||||
loading: false,
|
||||
authMethods: [],
|
||||
externalAuth: false,
|
||||
idle: false
|
||||
};
|
||||
|
||||
@@ -104,6 +107,11 @@ export function authReducer(state: any = initialState, action: AuthActions): Aut
|
||||
loading: true,
|
||||
});
|
||||
|
||||
case AuthActionTypes.SET_AUTH_COOKIE_STATUS:
|
||||
return Object.assign({}, state, {
|
||||
externalAuth: (action as SetAuthCookieStatus).payload
|
||||
});
|
||||
|
||||
case AuthActionTypes.AUTHENTICATED_ERROR:
|
||||
case AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON_ERROR:
|
||||
return Object.assign({}, state, {
|
||||
|
@@ -25,7 +25,7 @@ import {
|
||||
import { CookieService } from '../services/cookie.service';
|
||||
import {
|
||||
getAuthenticatedUserId,
|
||||
getAuthenticationToken,
|
||||
getAuthenticationToken, getExternalAuthCookieStatus,
|
||||
getRedirectUrl,
|
||||
isAuthenticated,
|
||||
isAuthenticatedLoaded,
|
||||
@@ -36,7 +36,7 @@ import { AppState } from '../../app.reducer';
|
||||
import {
|
||||
CheckAuthenticationTokenAction,
|
||||
RefreshTokenAction,
|
||||
ResetAuthenticationMessagesAction,
|
||||
ResetAuthenticationMessagesAction, SetAuthCookieStatus,
|
||||
SetRedirectUrlAction,
|
||||
SetUserAsIdleAction,
|
||||
UnsetUserAsIdleAction
|
||||
@@ -156,6 +156,15 @@ export class AuthService {
|
||||
return this.store.pipe(select(isAuthenticatedLoaded));
|
||||
}
|
||||
|
||||
public setExternalAuthStatus(external: boolean) {
|
||||
this.store.dispatch(new SetAuthCookieStatus(external));
|
||||
}
|
||||
|
||||
public isExternalAuthentication(): Observable<boolean> {
|
||||
return this.store.pipe(
|
||||
select(getExternalAuthCookieStatus));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the href link to authenticated user
|
||||
* @returns {string}
|
||||
|
@@ -116,6 +116,8 @@ const _getRedirectUrl = (state: AuthState) => state.redirectUrl;
|
||||
|
||||
const _getAuthenticationMethods = (state: AuthState) => state.authMethods;
|
||||
|
||||
const _getExternalAuthCookieStatus = (state: AuthState) => state.externalAuth;
|
||||
|
||||
/**
|
||||
* Returns true if the user is idle.
|
||||
* @function _isIdle
|
||||
@@ -178,6 +180,16 @@ export const isAuthenticated = createSelector(getAuthState, _isAuthenticated);
|
||||
*/
|
||||
export const isAuthenticatedLoaded = createSelector(getAuthState, _isAuthenticatedLoaded);
|
||||
|
||||
/**
|
||||
* Returns the authentication cookie status. Expect to be ture when external authentication
|
||||
* is used.
|
||||
* @function getExternalAuthCookieStatus
|
||||
* @param {AuthState} state
|
||||
* @param {any} props
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const getExternalAuthCookieStatus = createSelector(getAuthState, _getExternalAuthCookieStatus);
|
||||
|
||||
/**
|
||||
* Returns true if the authentication request is loading.
|
||||
* @function isAuthenticationLoading
|
||||
|
@@ -122,6 +122,13 @@ export class AuthServiceStub {
|
||||
checkAuthenticationCookie() {
|
||||
return;
|
||||
}
|
||||
setExternalAuthStatus(externalCookie: boolean) {
|
||||
return;
|
||||
}
|
||||
|
||||
isExternalAuthentication(): Observable<boolean> {
|
||||
return;
|
||||
}
|
||||
|
||||
retrieveAuthMethodsFromAuthStatus(status: AuthStatus) {
|
||||
return observableOf(authMethodsMock);
|
||||
|
@@ -26,10 +26,11 @@ import { AuthService } from '../../app/core/auth/auth.service';
|
||||
import { ThemeService } from '../../app/shared/theme-support/theme.service';
|
||||
import { StoreAction, StoreActionTypes } from '../../app/store.actions';
|
||||
import { coreSelector } from '../../app/core/core.selectors';
|
||||
import { find, map } from 'rxjs/operators';
|
||||
import { filter, find, map, switchMap } from 'rxjs/operators';
|
||||
import { isNotEmpty } from '../../app/shared/empty.util';
|
||||
import { logStartupMessage } from '../../../startup-message';
|
||||
import { MenuService } from '../../app/shared/menu/menu.service';
|
||||
import { RootDataService } from '../../app/core/data/root-data.service';
|
||||
|
||||
/**
|
||||
* Performs client-side initialization.
|
||||
@@ -51,6 +52,7 @@ export class BrowserInitService extends InitService {
|
||||
protected authService: AuthService,
|
||||
protected themeService: ThemeService,
|
||||
protected menuService: MenuService,
|
||||
private rootDatatService: RootDataService
|
||||
) {
|
||||
super(
|
||||
store,
|
||||
@@ -80,6 +82,7 @@ export class BrowserInitService extends InitService {
|
||||
return async () => {
|
||||
await this.loadAppState();
|
||||
this.checkAuthenticationToken();
|
||||
this.externalAuthCheck();
|
||||
this.initCorrelationId();
|
||||
|
||||
this.checkEnvironment();
|
||||
@@ -134,4 +137,24 @@ export class BrowserInitService extends InitService {
|
||||
protected initGoogleAnalytics() {
|
||||
this.googleAnalyticsService.addTrackingIdToPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* When authenticated during the external authentication flow invalidate
|
||||
* the cache so the app is rehydrated with fresh data.
|
||||
* @private
|
||||
*/
|
||||
private externalAuthCheck() {
|
||||
|
||||
this.authenticationReady$().pipe(
|
||||
switchMap(() => this.authService.isExternalAuthentication().pipe(
|
||||
filter((externalAuth: boolean) => externalAuth)
|
||||
))
|
||||
).subscribe(() => {
|
||||
this.authService.setExternalAuthStatus(false);
|
||||
this.rootDatatService.invalidateRootCache();
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user