Use status request to check an existing token and to retrieve auth methods available

This commit is contained in:
Giuseppe Digilio
2020-01-16 14:36:20 +01:00
parent 6f8f4b31bf
commit 6560d1d112
4 changed files with 62 additions and 11 deletions

View File

@@ -85,6 +85,15 @@ export class AuthInterceptor implements HttpInterceptor {
return http.url && http.url.endsWith('/authn/logout'); return http.url && http.url.endsWith('/authn/logout');
} }
/**
* Check if response is from a status request
*
* @param http
*/
private isStatusResponse(http: HttpRequest<any> | HttpResponseBase): boolean {
return http.url && http.url.endsWith('/authn/status');
}
/** /**
* Extract location url from the WWW-Authenticate header * Extract location url from the WWW-Authenticate header
* *
@@ -202,8 +211,10 @@ export class AuthInterceptor implements HttpInterceptor {
const authService = this.inj.get(AuthService); const authService = this.inj.get(AuthService);
const token = authService.getToken(); const token: AuthTokenInfo = authService.getToken();
let newReq; let newReq: HttpRequest<any>;
let updateReq: any = {};
let authorization: string;
if (authService.isTokenExpired()) { if (authService.isTokenExpired()) {
authService.setRedirectUrl(this.router.url); authService.setRedirectUrl(this.router.url);
@@ -224,11 +235,13 @@ export class AuthInterceptor implements HttpInterceptor {
} }
}); });
// Get the auth header from the service. // Get the auth header from the service.
const Authorization = authService.buildAuthHeader(token); authorization = authService.buildAuthHeader(token);
// Clone the request to add the new header. // Clone the request to add the new header.
newReq = req.clone({headers: req.headers.set('authorization', Authorization)}); newReq = req.clone({ headers: req.headers.set('authorization', authorization) });
} else { } else {
const updateReq = this.isAuthRequest(req) ? { withCredentials: true } : {}; if (this.isAuthRequest(req)) {
updateReq = { withCredentials: true };
}
newReq = req.clone(updateReq); newReq = req.clone(updateReq);
} }
@@ -237,19 +250,29 @@ export class AuthInterceptor implements HttpInterceptor {
// tap((response) => console.log('next.handle: ', response)), // tap((response) => console.log('next.handle: ', response)),
map((response) => { map((response) => {
// Intercept a Login/Logout response // Intercept a Login/Logout response
if (response instanceof HttpResponse && this.isSuccess(response) && (this.isLoginResponse(response) || this.isLogoutResponse(response))) { if (response instanceof HttpResponse && this.isSuccess(response) && this.isAuthRequest(response)) {
// It's a success Login/Logout response // It's a success Login/Logout response
let authRes: HttpResponse<any>; let authRes: HttpResponse<any>;
if (this.isLoginResponse(response)) { if (this.isLoginResponse(response)) {
// login successfully // login successfully
const newToken = response.headers.get('authorization'); const newToken = response.headers.get('authorization');
authRes = response.clone({body: this.makeAuthStatusObject(true, newToken)}); authRes = response.clone({
body: this.makeAuthStatusObject(true, newToken)
});
// clean eventually refresh Requests list // clean eventually refresh Requests list
this.refreshTokenRequestUrls = []; this.refreshTokenRequestUrls = [];
} else if (this.isStatusResponse(response)) {
authRes = response.clone({
body: Object.assign(response.body, {
authMethods: this.parseAuthMethodsFromHeaders(response.headers)
})
})
} else { } else {
// logout successfully // logout successfully
authRes = response.clone({body: this.makeAuthStatusObject(false)}); authRes = response.clone({
body: this.makeAuthStatusObject(false)
});
} }
return authRes; return authRes;
} else { } else {

View File

@@ -16,7 +16,13 @@ import { AuthStatus } from './models/auth-status.model';
import { AuthTokenInfo, TOKENITEM } from './models/auth-token-info.model'; import { AuthTokenInfo, TOKENITEM } from './models/auth-token-info.model';
import { isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../shared/empty.util'; import { isEmpty, isNotEmpty, isNotNull, isNotUndefined } from '../../shared/empty.util';
import { CookieService } from '../services/cookie.service'; import { CookieService } from '../services/cookie.service';
import { getAuthenticationToken, getRedirectUrl, isAuthenticated, isTokenRefreshing } from './selectors'; import {
getAuthenticationMethods,
getAuthenticationToken,
getRedirectUrl,
isAuthenticated,
isTokenRefreshing
} from './selectors';
import { AppState, routerStateSelector } from '../../app.reducer'; import { AppState, routerStateSelector } from '../../app.reducer';
import { import {
CheckAuthenticationTokenAction, CheckAuthenticationTokenAction,
@@ -28,6 +34,7 @@ import { Base64EncodeUrl } from '../../shared/utils/encode-decode.util';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { RouteService } from '../services/route.service'; import { RouteService } from '../services/route.service';
import { AuthMethod } from './models/auth.method'; import { AuthMethod } from './models/auth.method';
import { NormalizedAuthStatus } from './models/normalized-auth-status.model';
export const LOGIN_ROUTE = '/login'; export const LOGIN_ROUTE = '/login';
export const LOGOUT_ROUTE = '/logout'; export const LOGOUT_ROUTE = '/logout';
@@ -121,7 +128,14 @@ export class AuthService {
* Checks if token is present into the request cookie * Checks if token is present into the request cookie
*/ */
public checkAuthenticationCookie(): Observable<AuthStatus> { public checkAuthenticationCookie(): Observable<AuthStatus> {
return this.authRequestService.postToEndpoint('login'); // Determine if the user has an existing auth session on the server
const options: HttpOptions = Object.create({});
let headers = new HttpHeaders();
headers = headers.append('Accept', 'application/json');
options.headers = headers;
return this.authRequestService.getRequest('status', options).pipe(
map((status: NormalizedAuthStatus) => Object.assign(new AuthStatus(), status))
);
} }
/** /**
@@ -158,7 +172,13 @@ export class AuthService {
* Checks if token is present into browser storage and is valid. (NB Check is done only on SSR) * Checks if token is present into browser storage and is valid. (NB Check is done only on SSR)
*/ */
public checkAuthenticationToken() { public checkAuthenticationToken() {
this.store.dispatch(new CheckAuthenticationTokenAction()); this.store.pipe(
select(getAuthenticationMethods),
filter((authMethods: AuthMethod[]) => isEmpty(authMethods)),
take(1)
).subscribe(() => {
this.store.dispatch(new CheckAuthenticationTokenAction());
});
} }
/** /**

View File

@@ -4,6 +4,7 @@ import { mapsTo, relationship } from '../../cache/builders/build-decorators';
import { NormalizedObject } from '../../cache/models/normalized-object.model'; import { NormalizedObject } from '../../cache/models/normalized-object.model';
import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer'; import { IDToUUIDSerializer } from '../../cache/id-to-uuid-serializer';
import { EPerson } from '../../eperson/models/eperson.model'; import { EPerson } from '../../eperson/models/eperson.model';
import { AuthMethod } from './auth.method';
@mapsTo(AuthStatus) @mapsTo(AuthStatus)
@inheritSerialization(NormalizedObject) @inheritSerialization(NormalizedObject)
@@ -39,4 +40,9 @@ export class NormalizedAuthStatus extends NormalizedObject<AuthStatus> {
@autoserialize @autoserialize
eperson: string; eperson: string;
/**
* All authentication methods enabled at the backend
*/
@autoserialize
authMethods: AuthMethod[];
} }

View File

@@ -225,6 +225,8 @@ export class AuthPostRequest extends PostRequest {
} }
export class AuthGetRequest extends GetRequest { export class AuthGetRequest extends GetRequest {
forceBypassCache = true;
constructor(uuid: string, href: string, public options?: HttpOptions) { constructor(uuid: string, href: string, public options?: HttpOptions) {
super(uuid, href, null, options); super(uuid, href, null, options);
} }