WIP trying to get callbacks working.
This commit is contained in:
parent
2d570850f0
commit
4758f2adcc
|
@ -5,6 +5,11 @@ import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
|||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import {forkJoin} from 'rxjs';
|
||||
import {LighthouseService} from '../../services/lighthouse.service';
|
||||
import {LighthouseSourceMetadata} from '../../models/lighthouse/lighthouse-source-metadata';
|
||||
import {ToastNotification, ToastType} from '../../models/fasten/toast';
|
||||
import {ToastService} from '../../services/toast.service';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {Location} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-medical-sources-connected',
|
||||
|
@ -24,6 +29,10 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
private lighthouseApi: LighthouseService,
|
||||
private fastenApi: FastenApiService,
|
||||
private modalService: NgbModal,
|
||||
private toastService: ToastService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
|
@ -39,9 +48,220 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
const callbackSourceType = this.activatedRoute.snapshot.paramMap.get('source_type')
|
||||
if(callbackSourceType) {
|
||||
console.log("handle callback redirect from source")
|
||||
this.status[callbackSourceType] = "token"
|
||||
|
||||
//the structure of "availableSourceList" vs "connectedSourceList" sources is slightly different,
|
||||
//connectedSourceList contains a "source" field. The this.fastenApi.createSource() call in the callback function will set it.
|
||||
this.lighthouseApi.getLighthouseSource(callbackSourceType)
|
||||
.then((metadata) => {
|
||||
this.connectedSourceList.push({metadata: metadata})
|
||||
return this.callback(callbackSourceType)
|
||||
})
|
||||
.then(console.log)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* if the user is redirected to this page from the lighthouse, we'll need to process the "code" to retrieve the access token & refresh token.
|
||||
* @param sourceType
|
||||
*/
|
||||
public async callback(sourceType: string) {
|
||||
|
||||
//get the source metadata again
|
||||
await this.lighthouseApi.getLighthouseSource(sourceType)
|
||||
.then(async (sourceMetadata: LighthouseSourceMetadata) => {
|
||||
|
||||
//get required parameters from the URI and local storage
|
||||
const callbackUrlParts = new URL(window.location.href)
|
||||
const fragmentParams = new URLSearchParams(callbackUrlParts.hash.substring(1))
|
||||
const callbackCode = callbackUrlParts.searchParams.get("code") || fragmentParams.get("code")
|
||||
const callbackState = callbackUrlParts.searchParams.get("state") || fragmentParams.get("state")
|
||||
const callbackError = callbackUrlParts.searchParams.get("error") || fragmentParams.get("error")
|
||||
const callbackErrorDescription = callbackUrlParts.searchParams.get("error_description") || fragmentParams.get("error_description")
|
||||
|
||||
//reset the url, removing the params and fragment from the current url.
|
||||
const urlTree = this.router.createUrlTree(["/sources"],{
|
||||
relativeTo: this.activatedRoute,
|
||||
});
|
||||
this.location.replaceState(urlTree.toString());
|
||||
|
||||
const expectedSourceStateInfo = JSON.parse(localStorage.getItem(callbackState))
|
||||
localStorage.removeItem(callbackState)
|
||||
|
||||
if(callbackError && !callbackCode){
|
||||
//TOOD: print this message in the UI
|
||||
let errMsg = "an error occurred while authenticating to this source. Please try again later"
|
||||
console.error(errMsg, callbackErrorDescription)
|
||||
throw new Error(errMsg)
|
||||
}
|
||||
|
||||
console.log("callback code:", callbackCode)
|
||||
this.status[sourceType] = "token"
|
||||
|
||||
let payload: any
|
||||
payload = await this.lighthouseApi.swapOauthToken(sourceType, sourceMetadata,expectedSourceStateInfo, callbackCode)
|
||||
|
||||
if(!payload.access_token || payload.error){
|
||||
//if the access token is not set, then something is wrong,
|
||||
let errMsg = payload.error || "unable to retrieve access_token"
|
||||
console.error(errMsg)
|
||||
throw new Error(errMsg)
|
||||
}
|
||||
|
||||
//If payload.patient is not set, make sure we extract the patient ID from the id_token or make an introspection req
|
||||
if(!payload.patient && payload.id_token){
|
||||
//
|
||||
console.log("NO PATIENT ID present, decoding jwt to extract patient")
|
||||
//const introspectionResp = await Oauth.introspectionRequest(as, client, payload.access_token)
|
||||
//console.log(introspectionResp)
|
||||
let decodedIdToken = this.jwtDecode(payload.id_token)
|
||||
//nextGen uses fhirUser instead of profile.
|
||||
payload.patient = decodedIdToken["profile"] || decodedIdToken["fhirUser"]
|
||||
|
||||
if(payload.patient){
|
||||
payload.patient = payload.patient.replace(/^(Patient\/)/,'')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Create FHIR Client
|
||||
|
||||
const dbSourceCredential = new Source({
|
||||
source_type: sourceType,
|
||||
|
||||
authorization_endpoint: sourceMetadata.authorization_endpoint,
|
||||
token_endpoint: sourceMetadata.token_endpoint,
|
||||
introspection_endpoint: sourceMetadata.introspection_endpoint,
|
||||
userinfo_endpoint: sourceMetadata.userinfo_endpoint,
|
||||
api_endpoint_base_url: sourceMetadata.api_endpoint_base_url,
|
||||
client_id: sourceMetadata.client_id,
|
||||
redirect_uri: sourceMetadata.redirect_uri,
|
||||
scopes_supported: sourceMetadata.scopes_supported,
|
||||
issuer: sourceMetadata.issuer,
|
||||
grant_types_supported: sourceMetadata.grant_types_supported,
|
||||
response_types_supported: sourceMetadata.response_types_supported,
|
||||
aud: sourceMetadata.aud,
|
||||
code_challenge_methods_supported: sourceMetadata.code_challenge_methods_supported,
|
||||
confidential: sourceMetadata.confidential,
|
||||
cors_relay_required: sourceMetadata.cors_relay_required,
|
||||
|
||||
patient: payload.patient,
|
||||
access_token: payload.access_token,
|
||||
refresh_token: payload.refresh_token,
|
||||
id_token: payload.id_token,
|
||||
|
||||
// @ts-ignore - in some cases the getAccessTokenExpiration is a string, which cases failures to store Source in db.
|
||||
expires_at: parseInt(this.getAccessTokenExpiration(payload)),
|
||||
})
|
||||
|
||||
this.fastenApi.createSource(dbSourceCredential)
|
||||
.subscribe((resp) => {
|
||||
// const sourceSyncMessage = JSON.parse(msg) as SourceSyncMessage
|
||||
delete this.status[sourceType]
|
||||
// window.location.reload();
|
||||
// this.connectedSourceList.
|
||||
|
||||
//find the index of the "inprogress" source in the connected List, and then add this source to its source metadata.
|
||||
let foundSource = this.connectedSourceList.findIndex((item) => item.metadata.source_type == sourceType)
|
||||
this.connectedSourceList[foundSource].source = resp.source
|
||||
|
||||
console.log("source sync-all response:", resp.summary)
|
||||
|
||||
const toastNotification = new ToastNotification()
|
||||
toastNotification.type = ToastType.Success
|
||||
toastNotification.message = `Successfully connected ${sourceType}`
|
||||
|
||||
// const upsertSummary = sourceSyncMessage.response as UpsertSummary
|
||||
// if(upsertSummary && upsertSummary.totalResources != upsertSummary.updatedResources.length){
|
||||
// toastNotification.message += `\n (total: ${upsertSummary.totalResources}, updated: ${upsertSummary.updatedResources.length})`
|
||||
// } else if(upsertSummary){
|
||||
// toastNotification.message += `\n (total: ${upsertSummary.totalResources})`
|
||||
// }
|
||||
|
||||
this.toastService.show(toastNotification)
|
||||
},
|
||||
(err) => {
|
||||
delete this.status[sourceType]
|
||||
// window.location.reload();
|
||||
|
||||
const toastNotification = new ToastNotification()
|
||||
toastNotification.type = ToastType.Error
|
||||
toastNotification.message = `An error occurred while accessing ${sourceType}: ${err}`
|
||||
toastNotification.autohide = false
|
||||
this.toastService.show(toastNotification)
|
||||
console.error(err)
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
delete this.status[sourceType]
|
||||
// window.location.reload();
|
||||
|
||||
const toastNotification = new ToastNotification()
|
||||
toastNotification.type = ToastType.Error
|
||||
toastNotification.message = `An error occurred while accessing ${sourceType}: ${err}`
|
||||
toastNotification.autohide = false
|
||||
this.toastService.show(toastNotification)
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* https://github.com/smart-on-fhir/client-js/blob/8f64b770dbcd0abd30646e239cd446dfa4d831f6/src/lib.ts#L311
|
||||
* Decodes a JWT token and returns it's body.
|
||||
* @param token The token to read
|
||||
* @param env An `Adapter` or any other object that has an `atob` method
|
||||
* @category Utility
|
||||
*/
|
||||
private jwtDecode(token: string): Record<string, any> | null
|
||||
{
|
||||
const payload = token.split(".")[1];
|
||||
return payload ? JSON.parse(atob(payload)) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/smart-on-fhir/client-js/blob/8f64b770dbcd0abd30646e239cd446dfa4d831f6/src/lib.ts#L334
|
||||
* Given a token response, computes and returns the expiresAt timestamp.
|
||||
* Note that this should only be used immediately after an access token is
|
||||
* received, otherwise the computed timestamp will be incorrect.
|
||||
* @param tokenResponse
|
||||
* @param env
|
||||
*/
|
||||
private getAccessTokenExpiration(tokenResponse: any): number
|
||||
{
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
// Option 1 - using the expires_in property of the token response
|
||||
if (tokenResponse.expires_in) {
|
||||
return now + tokenResponse.expires_in;
|
||||
}
|
||||
|
||||
// Option 2 - using the exp property of JWT tokens (must not assume JWT!)
|
||||
if (tokenResponse.access_token) {
|
||||
let tokenBody = this.jwtDecode(tokenResponse.access_token);
|
||||
if (tokenBody && tokenBody['exp']) {
|
||||
return tokenBody['exp'];
|
||||
}
|
||||
}
|
||||
|
||||
// Option 3 - if none of the above worked set this to 5 minutes after now
|
||||
return now + 300;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Modal Window Functions
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public openModal(contentModalRef, sourceListItem: SourceListItem) {
|
||||
if(this.status[sourceListItem.metadata.source_type] || !sourceListItem.source){
|
||||
//if this source is currently "loading" dont open the modal window
|
||||
|
|
|
@ -4,11 +4,8 @@ import {FastenApiService} from '../../services/fasten-api.service';
|
|||
import {LighthouseSourceMetadata} from '../../models/lighthouse/lighthouse-source-metadata';
|
||||
import {Source} from '../../models/fasten/source';
|
||||
import {MetadataSource} from '../../models/fasten/metadata-source';
|
||||
import {ModalDismissReasons, NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {Location} from '@angular/common';
|
||||
import {ToastService} from '../../services/toast.service';
|
||||
import {ToastNotification, ToastType} from '../../models/fasten/toast';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {environment} from '../../../environments/environment';
|
||||
import {BehaviorSubject, forkJoin, Observable, Subject} from 'rxjs';
|
||||
import {
|
||||
|
@ -79,8 +76,6 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
private lighthouseApi: LighthouseService,
|
||||
private fastenApi: FastenApiService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private location: Location,
|
||||
private toastService: ToastService,
|
||||
private filterService: MedicalSourcesFilterService,
|
||||
private modalService: NgbModal,
|
||||
) { }
|
||||
|
@ -91,8 +86,14 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
//TODO: handle Callbacks from the source connect window
|
||||
const callbackSourceType = this.activatedRoute.snapshot.paramMap.get('source_type')
|
||||
if(callbackSourceType){
|
||||
console.error("TODO! handle callback redirect from source")
|
||||
} else {
|
||||
//move this source from available to connected (with a progress bar)
|
||||
//remove item from available sources list, add to connected sources.
|
||||
let inProgressAvailableIndex = this.availableSourceList.findIndex((item) => item.metadata.source_type == callbackSourceType)
|
||||
if(inProgressAvailableIndex > -1){
|
||||
let sourcesInProgress = this.availableSourceList.splice(inProgressAvailableIndex, 1);
|
||||
}
|
||||
|
||||
}
|
||||
//we're not in a callback redirect, lets load the sources
|
||||
if(this.activatedRoute.snapshot.queryParams['query']){
|
||||
this.searchTermUpdate.next(this.activatedRoute.snapshot.queryParams['query'])
|
||||
|
@ -118,61 +119,7 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
this.filterForm.patchValue(updatedForm, { emitEvent: false});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// OLD CODE - Should be refactored
|
||||
|
||||
|
||||
|
||||
|
||||
// this.loading = true
|
||||
// forkJoin([this.lighthouseApi.findLighthouseSources("", "", this.showHidden), this.fastenApi.getSources()]).subscribe(results => {
|
||||
// this.loading = false
|
||||
//
|
||||
// //handle connected sources sources
|
||||
// const connectedSources = results[1] as Source[]
|
||||
// forkJoin(connectedSources.map((source) => this.lighthouseApi.getLighthouseSource(source.source_type))).subscribe((connectedMetadata) => {
|
||||
// for(const ndx in connectedSources){
|
||||
// this.connectedSourceList.push({source: connectedSources[ndx], metadata: connectedMetadata[ndx]})
|
||||
// }
|
||||
// })
|
||||
//
|
||||
//
|
||||
// //handle source metadata map response
|
||||
// this.populateAvailableSourceList(results[0] as LighthouseSourceSearch)
|
||||
//
|
||||
//
|
||||
// //check if we've just started connecting a "source_type"
|
||||
// const callbackSourceType = this.route.snapshot.paramMap.get('source_type')
|
||||
// if(callbackSourceType){
|
||||
// this.status[callbackSourceType] = "token"
|
||||
//
|
||||
// //move this source from available to connected (with a progress bar)
|
||||
// //remove item from available sources list, add to connected sources.
|
||||
// let inProgressAvailableIndex = this.availableSourceList.findIndex((item) => item.metadata.source_type == callbackSourceType)
|
||||
// if(inProgressAvailableIndex > -1){
|
||||
// let sourcesInProgress = this.availableSourceList.splice(inProgressAvailableIndex, 1);
|
||||
//
|
||||
// }
|
||||
//
|
||||
// //the structure of "availableSourceList" vs "connectedSourceList" sources is slightly different,
|
||||
// //connectedSourceList contains a "source" field. The this.fastenApi.createSource() call in the callback function will set it.
|
||||
// this.lighthouseApi.getLighthouseSource(callbackSourceType)
|
||||
// .then((metadata) => {
|
||||
// this.connectedSourceList.push({metadata: metadata})
|
||||
// return this.callback(callbackSourceType)
|
||||
// })
|
||||
// .then(console.log)
|
||||
// }
|
||||
//
|
||||
// }, err => {
|
||||
// this.loading = false
|
||||
// })
|
||||
//
|
||||
//
|
||||
//register a callback for when the search box content changes
|
||||
this.searchTermUpdate
|
||||
.pipe(
|
||||
|
@ -336,153 +283,6 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
}
|
||||
|
||||
|
||||
//
|
||||
// /**
|
||||
// * if the user is redirected to this page from the lighthouse, we'll need to process the "code" to retrieve the access token & refresh token.
|
||||
// * @param sourceType
|
||||
// */
|
||||
// public async callback(sourceType: string) {
|
||||
//
|
||||
// //get the source metadata again
|
||||
// await this.lighthouseApi.getLighthouseSource(sourceType)
|
||||
// .then(async (sourceMetadata: LighthouseSourceMetadata) => {
|
||||
//
|
||||
// //get required parameters from the URI and local storage
|
||||
// const callbackUrlParts = new URL(window.location.href)
|
||||
// const fragmentParams = new URLSearchParams(callbackUrlParts.hash.substring(1))
|
||||
// const callbackCode = callbackUrlParts.searchParams.get("code") || fragmentParams.get("code")
|
||||
// const callbackState = callbackUrlParts.searchParams.get("state") || fragmentParams.get("state")
|
||||
// const callbackError = callbackUrlParts.searchParams.get("error") || fragmentParams.get("error")
|
||||
// const callbackErrorDescription = callbackUrlParts.searchParams.get("error_description") || fragmentParams.get("error_description")
|
||||
//
|
||||
// //reset the url, removing the params and fragment from the current url.
|
||||
// const urlTree = this.router.createUrlTree(["/sources"],{
|
||||
// relativeTo: this.route,
|
||||
// });
|
||||
// this.location.replaceState(urlTree.toString());
|
||||
//
|
||||
// const expectedSourceStateInfo = JSON.parse(localStorage.getItem(callbackState))
|
||||
// localStorage.removeItem(callbackState)
|
||||
//
|
||||
// if(callbackError && !callbackCode){
|
||||
// //TOOD: print this message in the UI
|
||||
// let errMsg = "an error occurred while authenticating to this source. Please try again later"
|
||||
// console.error(errMsg, callbackErrorDescription)
|
||||
// throw new Error(errMsg)
|
||||
// }
|
||||
//
|
||||
// console.log("callback code:", callbackCode)
|
||||
// this.status[sourceType] = "token"
|
||||
//
|
||||
// let payload: any
|
||||
// payload = await this.lighthouseApi.swapOauthToken(sourceType, sourceMetadata,expectedSourceStateInfo, callbackCode)
|
||||
//
|
||||
// if(!payload.access_token || payload.error){
|
||||
// //if the access token is not set, then something is wrong,
|
||||
// let errMsg = payload.error || "unable to retrieve access_token"
|
||||
// console.error(errMsg)
|
||||
// throw new Error(errMsg)
|
||||
// }
|
||||
//
|
||||
// //If payload.patient is not set, make sure we extract the patient ID from the id_token or make an introspection req
|
||||
// if(!payload.patient && payload.id_token){
|
||||
// //
|
||||
// console.log("NO PATIENT ID present, decoding jwt to extract patient")
|
||||
// //const introspectionResp = await Oauth.introspectionRequest(as, client, payload.access_token)
|
||||
// //console.log(introspectionResp)
|
||||
// let decodedIdToken = this.jwtDecode(payload.id_token)
|
||||
// //nextGen uses fhirUser instead of profile.
|
||||
// payload.patient = decodedIdToken["profile"] || decodedIdToken["fhirUser"]
|
||||
//
|
||||
// if(payload.patient){
|
||||
// payload.patient = payload.patient.replace(/^(Patient\/)/,'')
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// //Create FHIR Client
|
||||
//
|
||||
// const dbSourceCredential = new Source({
|
||||
// source_type: sourceType,
|
||||
//
|
||||
// authorization_endpoint: sourceMetadata.authorization_endpoint,
|
||||
// token_endpoint: sourceMetadata.token_endpoint,
|
||||
// introspection_endpoint: sourceMetadata.introspection_endpoint,
|
||||
// userinfo_endpoint: sourceMetadata.userinfo_endpoint,
|
||||
// api_endpoint_base_url: sourceMetadata.api_endpoint_base_url,
|
||||
// client_id: sourceMetadata.client_id,
|
||||
// redirect_uri: sourceMetadata.redirect_uri,
|
||||
// scopes_supported: sourceMetadata.scopes_supported,
|
||||
// issuer: sourceMetadata.issuer,
|
||||
// grant_types_supported: sourceMetadata.grant_types_supported,
|
||||
// response_types_supported: sourceMetadata.response_types_supported,
|
||||
// aud: sourceMetadata.aud,
|
||||
// code_challenge_methods_supported: sourceMetadata.code_challenge_methods_supported,
|
||||
// confidential: sourceMetadata.confidential,
|
||||
// cors_relay_required: sourceMetadata.cors_relay_required,
|
||||
//
|
||||
// patient: payload.patient,
|
||||
// access_token: payload.access_token,
|
||||
// refresh_token: payload.refresh_token,
|
||||
// id_token: payload.id_token,
|
||||
//
|
||||
// // @ts-ignore - in some cases the getAccessTokenExpiration is a string, which cases failures to store Source in db.
|
||||
// expires_at: parseInt(this.getAccessTokenExpiration(payload)),
|
||||
// })
|
||||
//
|
||||
// this.fastenApi.createSource(dbSourceCredential)
|
||||
// .subscribe((resp) => {
|
||||
// // const sourceSyncMessage = JSON.parse(msg) as SourceSyncMessage
|
||||
// delete this.status[sourceType]
|
||||
// // window.location.reload();
|
||||
// // this.connectedSourceList.
|
||||
//
|
||||
// //find the index of the "inprogress" source in the connected List, and then add this source to its source metadata.
|
||||
// let foundSource = this.connectedSourceList.findIndex((item) => item.metadata.source_type == sourceType)
|
||||
// this.connectedSourceList[foundSource].source = resp.source
|
||||
//
|
||||
// console.log("source sync-all response:", resp.summary)
|
||||
//
|
||||
// const toastNotification = new ToastNotification()
|
||||
// toastNotification.type = ToastType.Success
|
||||
// toastNotification.message = `Successfully connected ${sourceType}`
|
||||
//
|
||||
// // const upsertSummary = sourceSyncMessage.response as UpsertSummary
|
||||
// // if(upsertSummary && upsertSummary.totalResources != upsertSummary.updatedResources.length){
|
||||
// // toastNotification.message += `\n (total: ${upsertSummary.totalResources}, updated: ${upsertSummary.updatedResources.length})`
|
||||
// // } else if(upsertSummary){
|
||||
// // toastNotification.message += `\n (total: ${upsertSummary.totalResources})`
|
||||
// // }
|
||||
//
|
||||
// this.toastService.show(toastNotification)
|
||||
// },
|
||||
// (err) => {
|
||||
// delete this.status[sourceType]
|
||||
// // window.location.reload();
|
||||
//
|
||||
// const toastNotification = new ToastNotification()
|
||||
// toastNotification.type = ToastType.Error
|
||||
// toastNotification.message = `An error occurred while accessing ${sourceType}: ${err}`
|
||||
// toastNotification.autohide = false
|
||||
// this.toastService.show(toastNotification)
|
||||
// console.error(err)
|
||||
// });
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// delete this.status[sourceType]
|
||||
// // window.location.reload();
|
||||
//
|
||||
// const toastNotification = new ToastNotification()
|
||||
// toastNotification.type = ToastType.Error
|
||||
// toastNotification.message = `An error occurred while accessing ${sourceType}: ${err}`
|
||||
// toastNotification.autohide = false
|
||||
// this.toastService.show(toastNotification)
|
||||
// console.error(err)
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* this function is used to process manually "uploaded" FHIR bundle files, adding them to the database.
|
||||
|
@ -504,54 +304,4 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* https://github.com/smart-on-fhir/client-js/blob/8f64b770dbcd0abd30646e239cd446dfa4d831f6/src/lib.ts#L311
|
||||
* Decodes a JWT token and returns it's body.
|
||||
* @param token The token to read
|
||||
* @param env An `Adapter` or any other object that has an `atob` method
|
||||
* @category Utility
|
||||
*/
|
||||
private jwtDecode(token: string): Record<string, any> | null
|
||||
{
|
||||
const payload = token.split(".")[1];
|
||||
return payload ? JSON.parse(atob(payload)) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/smart-on-fhir/client-js/blob/8f64b770dbcd0abd30646e239cd446dfa4d831f6/src/lib.ts#L334
|
||||
* Given a token response, computes and returns the expiresAt timestamp.
|
||||
* Note that this should only be used immediately after an access token is
|
||||
* received, otherwise the computed timestamp will be incorrect.
|
||||
* @param tokenResponse
|
||||
* @param env
|
||||
*/
|
||||
private getAccessTokenExpiration(tokenResponse: any): number
|
||||
{
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
// Option 1 - using the expires_in property of the token response
|
||||
if (tokenResponse.expires_in) {
|
||||
return now + tokenResponse.expires_in;
|
||||
}
|
||||
|
||||
// Option 2 - using the exp property of JWT tokens (must not assume JWT!)
|
||||
if (tokenResponse.access_token) {
|
||||
let tokenBody = this.jwtDecode(tokenResponse.access_token);
|
||||
if (tokenBody && tokenBody['exp']) {
|
||||
return tokenBody['exp'];
|
||||
}
|
||||
}
|
||||
|
||||
// Option 3 - if none of the above worked set this to 5 minutes after now
|
||||
return now + 300;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue