Last active
January 31, 2026 02:37
-
-
Save CodingBash/459458ef3b477af162539ac896e056c3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { Injectable } from '@angular/core'; | |
| import { LocalStorageService } from 'ngx-localstorage'; | |
| import { WindowRef } from '../utilities/windowref.utils'; | |
| import { environment } from 'src/environments/environment'; | |
| import { HttpClient, HttpHeaders } from '@angular/common/http'; | |
| import { map } from 'rxjs/operators'; | |
| import { Observable } from 'rxjs'; | |
| import { UserPrincipal } from 'src/app/models/user-principal'; | |
| import { LabGroup } from '../models/lab-group'; | |
| import { Study } from '../models/study'; | |
| @Injectable({ | |
| providedIn: 'root' | |
| }) | |
| export class AuthenticationService { | |
| private gatewayCheckAuthenticatedUrl: string = environment.gatewayCheckAuthenticatedUrl; | |
| private gatewayGetUserUrl: string = environment.gatewayGetUserUrl; | |
| private gatewayLoginUrl: string = environment.gatewayLoginUrl; | |
| private gatewayLogoutUrl: string = environment.gatewayLogoutUrl; | |
| constructor( | |
| private winRef: WindowRef, | |
| private http: HttpClient | |
| ) { } | |
| public loginUserViaGateway(access_token: string) { | |
| const headers = new HttpHeaders({ | |
| 'Authorization': "Bearer " + access_token | |
| }) | |
| return this.http.post<UserPrincipal>(this.gatewayLoginUrl, null, {headers: headers, withCredentials: true}).pipe(map(response => this.mapUserPrincipalResponse(response))); | |
| } | |
| public logoutUserViaGateway(){ | |
| return this.http.get<boolean>(this.gatewayLogoutUrl, {withCredentials: true}); | |
| } | |
| public getCurrentUserViaGateway(): Observable<UserPrincipal> { | |
| return this.http.get<UserPrincipal>(this.gatewayGetUserUrl, {withCredentials: true}) | |
| .pipe( | |
| map(response => this.mapUserPrincipalResponse(response))); | |
| } | |
| public isAuthenticatedViaGateway(): Observable<boolean> { | |
| return this.http.get<boolean>(this.gatewayCheckAuthenticatedUrl, {withCredentials: true}); | |
| } | |
| /* | |
| * DEPRECATED - not checking authentication via local storage, see isAuthenticatedViaGateway | |
| */ | |
| public isAuthenticatedViaLocalStorage(): boolean { | |
| var token = this.winRef.nativeWindow.localStorage.getItem("CellxCognitoToken"); | |
| return token != null; | |
| } | |
| /* | |
| * DEPRECATED - no need to store token since we have the session ID given by the CellX API gateway | |
| */ | |
| public setToken(token: string): void { | |
| this.winRef.nativeWindow.localStorage.setItem("CellxCognitoToken", token); | |
| } | |
| private mapUserPrincipalResponse(response): UserPrincipal { | |
| console.log(response); | |
| let userPrincipal: UserPrincipal = new UserPrincipal(); | |
| userPrincipal.cognito_account_sub = response["cognito-user-sub"]; | |
| userPrincipal.cognito_account_email = response["cognito-user-email"]; | |
| userPrincipal.cognito_account_email_verified = response["cognito-user-email-verified"]; | |
| userPrincipal.cognito_account_name = response["cognito-user-name"]; | |
| userPrincipal.cognito_account_username = response["cognito-user-username"]; | |
| userPrincipal.cellx_account_account_id = response["cellx-user-account-id"]; | |
| userPrincipal.cellx_account_cognito_id = response["cellx-user-cognito-id"]; | |
| userPrincipal.cellx_account_roles = response["cellx-user-roles"].map(authority=>authority["authority"]); | |
| // TODO: Provide LabGroup::studyNamesAnd DisplayNames (or just a list of studies) | |
| userPrincipal.cellx_labgroups = response["cellx-groups"].map(labGroup => new LabGroup(labGroup["groupId"], labGroup["groupName"], null)); | |
| userPrincipal.cellx_accessible_studies = response["cellx-accessible-studies"].map(study => new Study(study["studyName"], study["studyInfo"], study["studyDisplayName"], study["embargoed"], study["createTime"], study["updateTime"])); | |
| console.log(userPrincipal); | |
| return userPrincipal; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { Component, OnInit } from '@angular/core'; | |
| import { Router, ActivatedRoute } from '@angular/router'; | |
| import { AuthenticationService } from 'src/app/services/authentication.service'; | |
| import { environment } from 'src/environments/environment'; | |
| import { UserPrincipalService } from 'src/app/services/user-principal.service'; | |
| import { RootNavItemsEmitterService } from 'src/app/services/root-nav-items-emitter.service'; | |
| //import { AlertService, AuthenticationService } from '../_services'; | |
| @Component({ | |
| selector: 'app-login', | |
| templateUrl: './login.component.html', | |
| styleUrls: ['./login.component.css'] | |
| }) | |
| export class LoginComponent implements OnInit { | |
| public cognitoLoginUrl: string = environment.cognitoLoginUrl; | |
| public redirectToLoginMessage: boolean = null; | |
| constructor( | |
| private route: ActivatedRoute, | |
| private router: Router, | |
| private authenticationService: AuthenticationService, | |
| private userPrincipalService: UserPrincipalService, | |
| private rootNavItemsEmitterService: RootNavItemsEmitterService | |
| ) {} | |
| ngOnInit() { | |
| /* | |
| * TODO: Can this be deletead | |
| * Determine if we are redirecting to login or to home. This determines what message is displayed on the screen. | |
| */ | |
| if(this.route.snapshot.fragment){ | |
| this.redirectToLoginMessage = false; | |
| } else { | |
| this.redirectToLoginMessage = true; | |
| } | |
| this.authenticationService.isAuthenticatedViaGateway().subscribe(isAuthenticated => { | |
| if(isAuthenticated){ // If the user is already authenticated | |
| if(this.userPrincipalService.userPrincipalSource.getValue() == null){ // But there is no userPrincipal stored | |
| this.authenticationService.getCurrentUserViaGateway().subscribe(userPrincipal => { // Refresh the userPrincipal by calling the user REST endpoint | |
| this.userPrincipalService.setUserPrincipal(userPrincipal); // Set the retrieved user principal | |
| this.rootNavItemsEmitterService.pingRootNavItemEvent(); | |
| this.router.navigate(['/home']); // userPrincipal now set, return client back to home page. | |
| }); | |
| } else { // And there is a userPrincipal | |
| this.router.navigate(['/home']); // Nothing to do - return them to the home page | |
| } | |
| // TODO: Optionally, readd a UserPrincipal to the UserPrincipalService | |
| } else if (this.route.snapshot.fragment){ // If a hash fragment exists in the URL (redirected back from Cognito with access token in hash fragment) | |
| /* | |
| * Extract the URL fragment and get the id_token param (ignore the token type and expiration time) | |
| * We ignore expiration since we will get new id_token whenever a back-end request fails. Though in the future, the expiration may be used to let Spring know how long to persist the id_token, and also to set an expiration for the token in local storage | |
| */ | |
| const fragment: string = this.route.snapshot.fragment; // Get fragment | |
| const rawParams : string[] = fragment.split("&"); // Split params | |
| let listParams : [string, string][] = rawParams.map(rawParam => { | |
| let splittedParam : string[]= rawParam.split("="); | |
| let param : [string, string] = [splittedParam[0], splittedParam[1]]; | |
| return param; | |
| }); // Split params into key and value | |
| let mapParams : Map<string, string> = new Map<string, string>(); // Initial map of params | |
| listParams.forEach(param => mapParams.set(param[0], param[1])); // Set map values from the list of params | |
| // Finally, get the access_token that was recieved from Cognito | |
| const access_token : string = mapParams.get("access_token"); | |
| // TODO: This is minor, but if there is no access_token param, then we should redirect to cognitoLoginUrl - perhaps fragment is recieved from somewhere else, though unlikely | |
| // Login using the access_token | |
| this.authenticationService.loginUserViaGateway(access_token).subscribe(userPrincipal => { | |
| // Set the user principal | |
| // TODO: Need to be more efficient with how we set userPrincipal and when we call isAuthenticated endpoint | |
| this.userPrincipalService.setUserPrincipal(userPrincipal); | |
| // Refresh the root nav items. | |
| this.rootNavItemsEmitterService.pingRootNavItemEvent(); | |
| this.router.navigate(['/home']) | |
| }); | |
| } else { // If not authenticated or not redirected from Cognito | |
| window.location.href = this.cognitoLoginUrl; // Redirect to the cognito login URL | |
| } | |
| }) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { Injectable } from '@angular/core'; | |
| import { UserPrincipal } from '../models/user-principal'; | |
| import { BehaviorSubject } from 'rxjs'; | |
| import { RootNavItemsEmitterService } from './root-nav-items-emitter.service'; | |
| @Injectable({ | |
| providedIn: 'root' | |
| }) | |
| export class UserPrincipalService { | |
| public userPrincipalSource = new BehaviorSubject<UserPrincipal>(null); | |
| public userPrincipal$ = this.userPrincipalSource.asObservable(); | |
| constructor(private rootNavItemsEmitterService: RootNavItemsEmitterService) { } | |
| public setUserPrincipal(userPrincipal: UserPrincipal) : void { | |
| this.userPrincipalSource.next(userPrincipal); | |
| this.rootNavItemsEmitterService.pingRootNavItemEvent(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { LabGroup } from './lab-group'; | |
| import { Study } from './study'; | |
| /** | |
| * Represents the Principal map from the CellX back-end | |
| * TODO: Some of the attributes here are unnecessary - they should be removed here and from the original REST endpoints | |
| */ | |
| export class UserPrincipal { | |
| /* | |
| * Cognito account properties | |
| */ | |
| cognito_account_sub: string; | |
| cognito_account_email: string; | |
| cognito_account_email_verified: boolean; | |
| cognito_account_name: string; | |
| cognito_account_username: string; | |
| /* | |
| * CellX account properties | |
| */ | |
| cellx_account_account_id: string; | |
| cellx_account_cognito_id: string; | |
| cellx_account_roles: string[]; | |
| /* | |
| * Cellx user access control properties | |
| * TODO: This has yet to be implemented. First need to implement on back-end, then update the HttpClient mapper. | |
| */ | |
| cellx_labgroups: LabGroup[]; | |
| cellx_accessible_studies: Study[] | |
| constructor(){} | |
| /* | |
| * TODO: For some odd reason, if the admin role ever changes (i.e. adding more admin types), then this method needs to be updated. | |
| * TODO: Changing admin role indicators is a breaking change, so an implementation downtime would be required. | |
| * TODO: Maybe consider retrieving role names via SQL directly (via REST endpoint) instead of hard code? Low priority. | |
| */ | |
| isAdmin() { | |
| if(this.cellx_account_roles){ | |
| return this.cellx_account_roles.includes("ROLE_SITE_ADMIN"); // TODO: This needs to be in a constants file | |
| } else { | |
| return false; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment