mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 22:01:42 +03:00
Save cognito access token to ~/.enso/credentials (#6251)
To allow libs for using Cloud API they need to have access to the JWT token set by cognito. After talking to @jdunkerley and finding out it can not be obtained from localStorage we agreed to dump it to the file. This PR introduces simple saving jwt access token to ~/.enso/credentials (system agnostic)
This commit is contained in:
parent
72ae10c8e2
commit
467d3df4c2
@ -74,6 +74,9 @@
|
||||
|
||||
import * as electron from 'electron'
|
||||
import opener from 'opener'
|
||||
import * as fs from 'node:fs'
|
||||
import * as os from 'node:os'
|
||||
import * as path from 'node:path'
|
||||
|
||||
import * as common from 'enso-common'
|
||||
import * as contentConfig from 'enso-content-config'
|
||||
@ -104,6 +107,7 @@ const OPEN_URL_EVENT = 'open-url'
|
||||
export function initModule(window: () => electron.BrowserWindow) {
|
||||
initIpc()
|
||||
initOpenUrlListener(window)
|
||||
initSaveAccessTokenListener()
|
||||
}
|
||||
|
||||
/** Registers an Inter-Process Communication (IPC) channel between the Electron application and the
|
||||
@ -139,3 +143,37 @@ function initOpenUrlListener(window: () => electron.BrowserWindow) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** Registers a listener that fires a callback for `save-access-token` events.
|
||||
*
|
||||
* This listener is used to save given access token to credentials file to be later used by enso backend.
|
||||
*
|
||||
* Credentials file is placed in users home directory in `.enso` subdirectory in `credentials` file. */
|
||||
function initSaveAccessTokenListener() {
|
||||
electron.ipcMain.on(ipc.Channel.saveAccessToken, (event, accessToken: string) => {
|
||||
/** Enso home directory for credentials file. */
|
||||
const ensoCredentialsDirectoryName = '.enso'
|
||||
/** Enso credentials file. */
|
||||
const ensoCredentialsFileName = 'credentials'
|
||||
/** System agnostic credentials directory home path. */
|
||||
const ensoCredentialsHomePath = path.join(os.homedir(), ensoCredentialsDirectoryName)
|
||||
|
||||
fs.mkdir(ensoCredentialsHomePath, { recursive: true }, err => {
|
||||
if (err) {
|
||||
logger.error(`Couldn't create ${ensoCredentialsDirectoryName} directory.`)
|
||||
} else {
|
||||
fs.writeFile(
|
||||
path.join(ensoCredentialsHomePath, ensoCredentialsFileName),
|
||||
accessToken,
|
||||
err => {
|
||||
if (err) {
|
||||
logger.error(`Could not write to ${ensoCredentialsFileName} file.`)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
event.preventDefault()
|
||||
})
|
||||
}
|
||||
|
@ -19,4 +19,6 @@ export enum Channel {
|
||||
setDeepLinkHandler = 'set-deep-link-handler',
|
||||
/** Channel for signaling that a deep link to this application was opened. */
|
||||
openDeepLink = 'open-deep-link',
|
||||
/** Channel for signaling that access token be saved to a credentials file. */
|
||||
saveAccessToken = 'save-access-token',
|
||||
}
|
||||
|
@ -105,5 +105,12 @@ const AUTHENTICATION_API = {
|
||||
electron.ipcRenderer.on(ipc.Channel.openDeepLink, (_event, url: string) => {
|
||||
callback(url)
|
||||
}),
|
||||
/** Saves the access token to a credentials file.
|
||||
*
|
||||
* Enso backend doesn't have access to Electron localStorage so we need to save access token to a file.
|
||||
* Then the token will be used to sign cloud API requests. */
|
||||
saveAccessToken: (accessToken: string) => {
|
||||
electron.ipcRenderer.send(ipc.Channel.saveAccessToken, accessToken)
|
||||
},
|
||||
}
|
||||
electron.contextBridge.exposeInMainWorld(AUTHENTICATION_API_KEY, AUTHENTICATION_API)
|
||||
|
@ -133,7 +133,7 @@ export class Cognito {
|
||||
constructor(
|
||||
private readonly logger: loggerProvider.Logger,
|
||||
private readonly platform: platformModule.Platform,
|
||||
amplifyConfig: config.AmplifyConfig
|
||||
private readonly amplifyConfig: config.AmplifyConfig
|
||||
) {
|
||||
/** Amplify expects `Auth.configure` to be called before any other `Auth` methods are
|
||||
* called. By wrapping all the `Auth` methods we care about and returning an `Cognito` API
|
||||
@ -143,6 +143,14 @@ export class Cognito {
|
||||
amplify.Auth.configure(nestedAmplifyConfig)
|
||||
}
|
||||
|
||||
/** Saves the access token to a file for further reuse. */
|
||||
|
||||
saveAccessToken(accessToken: string) {
|
||||
if (this.amplifyConfig.accessTokenSaver) {
|
||||
this.amplifyConfig.accessTokenSaver(accessToken)
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the current {@link UserSession}, or `None` if the user is not logged in.
|
||||
*
|
||||
* Will refresh the {@link UserSession} if it has expired. */
|
||||
@ -150,7 +158,7 @@ export class Cognito {
|
||||
return userSession()
|
||||
}
|
||||
|
||||
/** Sign up with with username and password.
|
||||
/** Sign up with username and password.
|
||||
*
|
||||
* Does not rely on federated identity providers (e.g., Google or GitHub). */
|
||||
signUp(username: string, password: string) {
|
||||
|
@ -70,6 +70,9 @@ export type OAuthRedirect = newtype.Newtype<string, 'OAuthRedirect'>
|
||||
* we want to open OAuth URLs in the system browser. This is because the user can't be expected to
|
||||
* trust their credentials to an Electron app. */
|
||||
export type OAuthUrlOpener = (url: string, redirectUrl: string) => void
|
||||
/** A function used to save access token to a Enso credentials file. The token is used by the engine to issue
|
||||
* http request to cloud API. */
|
||||
export type AccessTokenSaver = (accessToken: string) => void
|
||||
/** Function used to register a callback. The callback will get called when a deep link is received
|
||||
* by the app. This is only used in the desktop app (i.e., not in the cloud). This is used when the
|
||||
* user is redirected back to the app from the system browser, after completing an OAuth flow. */
|
||||
@ -90,6 +93,7 @@ export interface AmplifyConfig {
|
||||
userPoolId: UserPoolId
|
||||
userPoolWebClientId: UserPoolWebClientId
|
||||
urlOpener: OAuthUrlOpener | null
|
||||
accessTokenSaver: AccessTokenSaver | null
|
||||
domain: OAuthDomain
|
||||
scope: OAuthScope[]
|
||||
redirectSignIn: OAuthRedirect
|
||||
|
@ -172,6 +172,9 @@ export function AuthProvider(props: AuthProviderProps) {
|
||||
organization,
|
||||
}
|
||||
|
||||
/** Save access token so can be reused by Enso backend. */
|
||||
cognito.saveAccessToken(accessToken)
|
||||
|
||||
/** Execute the callback that should inform the Electron app that the user has logged in.
|
||||
* This is done to transition the app from the authentication/dashboard view to the IDE. */
|
||||
onAuthenticated()
|
||||
|
@ -133,6 +133,7 @@ function loadAmplifyConfig(
|
||||
/** Load the environment-specific Amplify configuration. */
|
||||
const baseConfig = AMPLIFY_CONFIGS[config.ENVIRONMENT]
|
||||
let urlOpener = null
|
||||
let accessTokenSaver = null
|
||||
if (platform === platformModule.Platform.desktop) {
|
||||
/** If we're running on the desktop, we want to override the default URL opener for OAuth
|
||||
* flows. This is because the default URL opener opens the URL in the desktop app itself,
|
||||
@ -144,6 +145,10 @@ function loadAmplifyConfig(
|
||||
* we avoid unnecessary reloads/refreshes caused by redirects. */
|
||||
urlOpener = openUrlWithExternalBrowser
|
||||
|
||||
/** When running on destop we want to have option to save access token to a file,
|
||||
* so it can be later reuse when issuing requests to Cloud API. */
|
||||
accessTokenSaver = saveAccessToken
|
||||
|
||||
/** To handle redirects back to the application from the system browser, we also need to
|
||||
* register a custom URL handler. */
|
||||
setDeepLinkHandler(logger, navigate)
|
||||
@ -154,6 +159,7 @@ function loadAmplifyConfig(
|
||||
...baseConfig,
|
||||
...platformConfig,
|
||||
urlOpener,
|
||||
accessTokenSaver,
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,6 +167,10 @@ function openUrlWithExternalBrowser(url: string) {
|
||||
window.authenticationApi.openUrlInSystemBrowser(url)
|
||||
}
|
||||
|
||||
function saveAccessToken(accessToken: string) {
|
||||
window.authenticationApi.saveAccessToken(accessToken)
|
||||
}
|
||||
|
||||
/** Set the callback that will be invoked when a deep link to the application is opened.
|
||||
*
|
||||
* Typically this callback is invoked when the user is redirected back to the app after:
|
||||
|
2
app/ide-desktop/lib/types/globals.d.ts
vendored
2
app/ide-desktop/lib/types/globals.d.ts
vendored
@ -38,6 +38,8 @@ interface AuthenticationApi {
|
||||
/** Set the callback to be called when the system browser redirects back to a URL in the app,
|
||||
* via a deep link. See {@link setDeepLinkHandler} for details. */
|
||||
setDeepLinkHandler: (callback: (url: string) => void) => void
|
||||
/** Saves the access token to a file. */
|
||||
saveAccessToken: (access_token: string) => void
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
Loading…
Reference in New Issue
Block a user