Skip to content
Open
13 changes: 8 additions & 5 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
this.subs.add(
this.store$
.select(userStore.getUserAuth)
.pipe(filter((userAuth) => !!userAuth.token))
.pipe(filter((userAuth) => !!userAuth?.token))
.subscribe((_) => this.store$.dispatch(new userStore.LoadProfile())),
);

Expand Down Expand Up @@ -361,10 +361,13 @@ export class AppComponent implements OnInit, OnDestroy, AfterViewInit {
),
);

const user = this.authService.getUser();
if (user.id) {
this.store$.dispatch(new userStore.Login(user));
}
this.subs.add(
this.authService.getUser().subscribe((user) => {
if (user?.id) {
this.store$.dispatch(new userStore.Login(user));
}
}),
);

this.subs.add(
this.actions$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
AsfApiService,
EnvironmentService,
ScreenSizeService,
UserDataService,
} from '@services';
import {
CMRProduct,
Expand Down Expand Up @@ -79,6 +80,7 @@ declare global {
})
export class HeaderButtonsComponent implements OnInit, OnDestroy {
authService = inject(AuthService);
userData = inject(UserDataService);
env = inject(EnvironmentService);
private http = inject(HttpClient);
asfApiService = inject(AsfApiService);
Expand Down Expand Up @@ -138,9 +140,9 @@ export class HeaderButtonsComponent implements OnInit, OnDestroy {

ngOnInit() {
this.subs.add(
this.store$
.select(userStore.getUserAuth)
.subscribe((user) => (this.userAuth = user)),
this.store$.select(userStore.getUserAuth).subscribe((user) => {
this.userAuth = user;
}),
);

this.subs.add(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="dialog-header">
<div mat-dialog-title class="dialog-title-overrides">
{{ 'PREFERENCES_FOR' | translate }} {{ userAuth.id }}
{{ 'PREFERENCES_FOR' | translate }} {{ userAuth?.id }}
</div>

<div class="dl-close-x" (click)="onCloseDownloadQueue()">
Expand Down
1 change: 1 addition & 0 deletions src/app/models/user.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface UserAuth {
id: string | null;
token: string | null;
groups: URSGroup[];
exp: number;
}

export interface UserProfile {
Expand Down
137 changes: 71 additions & 66 deletions src/app/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { interval, Subject, Observable, of } from 'rxjs';
import { map, takeUntil, take, filter, catchError } from 'rxjs/operators';
import {
map,
takeUntil,
take,
catchError,
filter,
switchMap,
retry,
} from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppState } from '@store';

import { EnvironmentService } from './environment.service';
import jwt_decode from 'jwt-decode';
import * as userStore from '@store/user';

import * as models from '@models';
Expand All @@ -21,18 +28,25 @@ export class AuthService {
private http = inject(HttpClient);
private notificationService = inject(NotificationService);
private store$ = inject<Store<AppState>>(Store);
private existingUserInfo: models.UserAuth = null;

private bc: BroadcastChannel;
constructor() {
if (typeof BroadcastChannel !== 'undefined') {
this.bc = new BroadcastChannel('asf-vertex');
this.bc.onmessage = (_event: MessageEvent) => {
const user = this.getUser();
if (!user.id) {
this.store$.dispatch(new userStore.Logout());
} else {
this.store$.dispatch(new userStore.Login(user));
}
this.getUser()
.pipe(
take(1),
map((user) => {
if (!user?.id) {
this.store$.dispatch(new userStore.Logout());
} else {
this.store$.dispatch(new userStore.Login(user));
}
}),
)
.subscribe();
};
}
}
Expand Down Expand Up @@ -65,29 +79,25 @@ export class AuthService {
);

const loginWindowClosed = new Subject<void>();

return interval(500).pipe(
takeUntil(loginWindowClosed),
map((_) => {
let user = null;

switchMap((_) => {
if (loginWindow.closed) {
loginWindowClosed.next();
return this.getUser();
}

try {
if (loginWindow.location.host === window.location.host) {
loginWindow.close();
user = this.getUser();
this.bc.postMessage({
event: 'login',
});
return this.getUser();
}
} catch (_e) {
// Do nothing
}

return user;
return of(null);
}),
catchError((_) => {
this.notificationService.error('Trouble logging in', 'Error', {
Expand All @@ -103,7 +113,7 @@ export class AuthService {

public logout$(): Observable<models.UserAuth> {
return this.http
.get(`${this.authUrl}/loginservice/logout`, {
.get(`${this.authUrl}/logout`, {
responseType: 'text',
withCredentials: true,
})
Expand All @@ -112,70 +122,65 @@ export class AuthService {
this.bc.postMessage({
event: 'logout',
});
return this.getUser();
return this.existingUserInfo;
}),
catchError((_) => {
this.notificationService.error('Trouble logging out', 'Error', {
timeOut: 5000,
});
return of(this.getUser());
// For now this always throws a CORS error, but it does still logout successfully
// this.notificationService.error('Trouble logging out', 'Error', {
// timeOut: 5000,
// });
return of(this.existingUserInfo);
}),
take(1),
);
}

public getUser(): models.UserAuth {
const cookies = this.loadCookies();
const token = cookies['asf-urs'];

if (!token) {
return this.nullUser();
}
try {
const user = jwt_decode(token);

if (this.isExpired(user)) {
return this.nullUser();
}

setTimeout(
() => {
this.store$.dispatch(new userStore.Logout());
this.notificationService.info(
'Session Expired',
'Please login again',
public getUser(): Observable<models.UserAuth | null> {
return this.http
.get<models.UserAuth>(`${this.env.currentEnv.user_data}/info/cookie`, {
withCredentials: true,
})
.pipe(
retry({
count: 3,
delay: 100,
}),
map((user) => {
return this.makeUser(
user['urs-user-id'],
user['urs-groups'],
user['token'],
user['exp'],
);
}),
map((final) => {
this.existingUserInfo = final;
setTimeout(
() => {
this.store$.dispatch(new userStore.Logout());
this.notificationService.info(
'Session Expired',
'Please login again',
);
},
final.exp * 1000 - Date.now(),
);
},
user.exp * 1000 - Date.now(),
return final;
}),
catchError((_error) => {
console.error('Failed to get user info');
return of(null);
}),
take(1),
);

return this.makeUser(user['urs-user-id'], user['urs-groups'], token);
} catch (_error) {
return this.nullUser();
}
}

private makeUser(
id: string,
groups: models.URSGroup[],
token: string,
exp: number,
): models.UserAuth {
return { id, token, groups };
}

private nullUser(): models.UserAuth {
return { id: null, token: null, groups: [] };
}

private isExpired(userToken): boolean {
return Date.now() > userToken.exp * 1000;
}

private loadCookies() {
return document.cookie
.split(';')
.map((s) => s.trim().split('='))
.map(([name, val]) => ({ [name]: val }))
.reduce((allCookies, cookie) => ({ ...allCookies, ...cookie }));
return { id, token, groups, exp };
}
}
4 changes: 2 additions & 2 deletions src/app/services/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export const env = {
test: {
api: 'https://api-test.asf.alaska.edu',
api_maturity: 'prod',
auth: 'https://auth.asf.alaska.edu',
auth: 'https://cumulus.asf.alaska.edu',
urs: 'https://urs.earthdata.nasa.gov',
urs_client_id: 'BO_n7nTIlMljdvU6kRRB3g',
banner: 'https://banners.asf.alaska.edu',
user_data: 'https://cgdjuem3wc.execute-api.us-east-1.amazonaws.com/prod/',
user_data: 'https://appdata-test.asf.alaska.edu',
unzip: 'https://unzip.asf.alaska.edu',
bulk_download: 'https://bulk-download.asf.alaska.edu',
},
Expand Down
2 changes: 1 addition & 1 deletion src/app/services/environment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface Environment {
api_maturity?: string;
urs_client_id: string;
unzip: string;
datapool: string;
datapool?: string;
banner: string;
user_data: string;
bulk_download: string;
Expand Down
2 changes: 1 addition & 1 deletion src/app/services/envs/env-devel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const env = {
urs: 'https://urs.earthdata.nasa.gov',
urs_client_id: 'BO_n7nTIlMljdvU6kRRB3g',
banner: 'https://banners.asf.alaska.edu',
user_data: 'https://cgdjuem3wc.execute-api.us-east-1.amazonaws.com/prod/',
user_data: 'https://appdata-test.asf.alaska.edu',
unzip: 'https://unzip.asf.alaska.edu',
bulk_download: 'https://bulk-download.asf.alaska.edu',
},
Expand Down
4 changes: 2 additions & 2 deletions src/app/services/envs/env-prod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export const env = {
test: {
api: 'https://api-test.asf.alaska.edu',
api_maturity: 'test',
auth: 'https://auth.asf.alaska.edu',
auth: 'https://cumulus.asf.alaska.edu',
urs: 'https://urs.earthdata.nasa.gov',
urs_client_id: 'BO_n7nTIlMljdvU6kRRB3g',
banner: 'https://banners.asf.alaska.edu',
user_data: 'https://cgdjuem3wc.execute-api.us-east-1.amazonaws.com/prod/',
user_data: 'https://appdata-test.asf.alaska.edu',
unzip: 'https://unzip.asf.alaska.edu',
bulk_download: 'https://bulk-download.asf.alaska.edu',
},
Expand Down
4 changes: 2 additions & 2 deletions src/app/services/envs/env-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export const env = {
test: {
api: 'https://api-test.asf.alaska.edu',
api_maturity: 'prod',
auth: 'https://auth.asf.alaska.edu',
auth: 'https://cumulus.asf.alaska.edu',
urs: 'https://urs.earthdata.nasa.gov',
urs_client_id: 'BO_n7nTIlMljdvU6kRRB3g',
banner: 'https://banners.asf.alaska.edu',
user_data: 'https://cgdjuem3wc.execute-api.us-east-1.amazonaws.com/prod/',
user_data: 'https://appdata-test.asf.alaska.edu',
unzip: 'https://unzip.asf.alaska.edu',
bulk_download: 'https://bulk-download.asf.alaska.edu',
},
Expand Down
Loading