From 8babd6494f7c046206997837c77f33fd801dccb7 Mon Sep 17 00:00:00 2001 From: Ryan Slatten Date: Wed, 23 Oct 2024 10:59:15 -0400 Subject: [PATCH] initial commit --- plugins/arcgis/service/src/ArcGISConfig.ts | 5 ++- .../src/ArcGISIdentityManagerFactory.ts | 44 +++++++++++++------ plugins/arcgis/service/src/index.ts | 6 +-- .../projects/main/src/lib/arc.service.ts | 18 ++++---- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/plugins/arcgis/service/src/ArcGISConfig.ts b/plugins/arcgis/service/src/ArcGISConfig.ts index 5451fcc27..5d3bb6977 100644 --- a/plugins/arcgis/service/src/ArcGISConfig.ts +++ b/plugins/arcgis/service/src/ArcGISConfig.ts @@ -106,11 +106,14 @@ export interface UsernamePasswordAuthConfig { * Contains OAuth authentication configuration. */ export interface OAuthAuthConfig { + type: AuthType.OAuth + /** * The Client Id for OAuth */ clientId: string + /** * The redirectUri for OAuth */ @@ -121,7 +124,7 @@ export interface OAuthAuthConfig { */ authToken?: string - /** + /** * The expiration date for the temporary token */ authTokenExpires?: number diff --git a/plugins/arcgis/service/src/ArcGISIdentityManagerFactory.ts b/plugins/arcgis/service/src/ArcGISIdentityManagerFactory.ts index e54b274bf..586610df7 100644 --- a/plugins/arcgis/service/src/ArcGISIdentityManagerFactory.ts +++ b/plugins/arcgis/service/src/ArcGISIdentityManagerFactory.ts @@ -1,13 +1,14 @@ import { ArcGISIdentityManager } from "@esri/arcgis-rest-request" import { ArcGISAuthConfig, AuthType, FeatureServiceConfig, OAuthAuthConfig, TokenAuthConfig, UsernamePasswordAuthConfig } from './ArcGISConfig' import { HttpClient } from "./HttpClient"; +import { ObservationProcessor } from "./ObservationProcessor"; interface ArcGISIdentityManagerFactory { - create(portal: string, server: string, config: ArcGISAuthConfig, httpClient?: HttpClient): Promise + create(portal: string, server: string, config: ArcGISAuthConfig, httpClient?: HttpClient, processor?: ObservationProcessor): Promise } const OAuthIdentityManagerFactory: ArcGISIdentityManagerFactory = { - async create(portal: string, server: string, auth: OAuthAuthConfig, httpClient: HttpClient): Promise { + async create(portal: string, server: string, auth: OAuthAuthConfig, httpClient: HttpClient, processor: ObservationProcessor): Promise { console.debug('Client ID provided for authentication') const { clientId, authToken, authTokenExpires, refreshToken, refreshTokenExpires } = auth @@ -22,14 +23,30 @@ const OAuthIdentityManagerFactory: ArcGISIdentityManagerFactory = { } else if (refreshToken && new Date(refreshTokenExpires || 0) > new Date()) { // TODO: find a way without using constructor nor httpClient const url = `${portal}/oauth2/token?client_id=${clientId}&refresh_token=${refreshToken}&grant_type=refresh_token` - const response = await httpClient.sendGet(url) - // TODO: error handling - return ArcGISIdentityManager.fromToken({ - clientId: clientId, - token: response.access_token, - portal: portal - }); - // TODO: update authToken to new token + try { + const response = await httpClient.sendGet(url) + // Update authToken to new token + const config = await processor.safeGetConfig(); + let service = config.featureServices.find(service => service.url === portal)?.auth as OAuthAuthConfig; + const date = new Date(); + date.setSeconds(date.getSeconds() + response.expires_in || 0); + service = { + ...service, + authToken: response.access_token, + authTokenExpires: date.getTime() + } + + await processor.putConfig(config) + return ArcGISIdentityManager.fromToken({ + clientId: clientId, + token: response.access_token, + tokenExpires: date, + portal: portal + }); + } catch (error) { + throw new Error('Error occurred when using refresh token') + } + } else { // TODO the config, we need to let the user know UI side they need to authenticate again throw new Error('Refresh token missing or expired') @@ -46,7 +63,7 @@ const TokenIdentityManagerFactory: ArcGISIdentityManagerFactory = { server: server, // TODO: what do we really want to do here? esri package seems to need this optional parameter. // Use authTokenExpires if defined, otherwise set to now plus a day - tokenExpires: auth.authTokenExpires ? new Date(auth.authTokenExpires) : new Date(Date.now() + 24 * 60 * 60 * 1000) + tokenExpires: auth.authTokenExpires ? new Date(auth.authTokenExpires) : new Date(Date.now() + 24 * 60 * 60 * 1000) }) return identityManager } @@ -68,7 +85,8 @@ const authConfigMap: { [key: string]: ArcGISIdentityManagerFactory } = { export function getIdentityManager( config: FeatureServiceConfig, - httpClient: HttpClient // TODO remove in favor of an open source lib like axios + httpClient: HttpClient, // TODO remove in favor of an open source lib like axios + processor: ObservationProcessor ): Promise { const auth = config.auth const authType = config.auth?.type @@ -79,7 +97,7 @@ export function getIdentityManager( if (!factory) { throw new Error(`No factory found for type ${authType}`) } - return factory.create(getPortalUrl(config.url), getServerUrl(config.url), auth, httpClient) + return factory.create(getPortalUrl(config.url), getServerUrl(config.url), auth, httpClient, processor) } diff --git a/plugins/arcgis/service/src/index.ts b/plugins/arcgis/service/src/index.ts index ce34fce3d..ad93a4716 100644 --- a/plugins/arcgis/service/src/index.ts +++ b/plugins/arcgis/service/src/index.ts @@ -120,7 +120,7 @@ const arcgisPluginHooks: InitPluginHook = { } await processor.putConfig(config) - + // TODO: This seems like a bad idea to send the access tokens to the front end. It has no use for them and could potentially be a security concern res.send(` @@ -181,7 +181,7 @@ const arcgisPluginHooks: InitPluginHook = { try { const httpClient = new HttpClient(console) // Create the IdentityManager instance to validate credentials - await getIdentityManager(service!, httpClient) + await getIdentityManager(service!, httpClient, processor) let existingService = config.featureServices.find(service => service.url === url) if (existingService) { existingService = { ...existingService } @@ -206,7 +206,7 @@ const arcgisPluginHooks: InitPluginHook = { const httpClient = new HttpClient(console) try { - const identityManager = await getIdentityManager(featureService, httpClient) + const identityManager = await getIdentityManager(featureService, httpClient, processor) const response = await request(url, { authentication: identityManager }) diff --git a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts index adf4355a1..bd2b831d6 100644 --- a/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts +++ b/plugins/arcgis/web-app/projects/main/src/lib/arc.service.ts @@ -61,15 +61,17 @@ export class ArcService implements ArcServiceInterface { const oauthWindow = window.open(url, "_blank"); const listener = (event: any) => { - window.removeEventListener('message', listener, false); - - if (event.origin !== window.location.origin) { - subject.error('target origin mismatch') + if (event.data.url) { + window.removeEventListener('message', listener, false); + + if (event.origin !== window.location.origin) { + subject.error('target origin mismatch') + } + + subject.next(event.data) + + oauthWindow?.close(); } - - subject.next(event.data) - - oauthWindow?.close(); } window.addEventListener('message', listener, false);