diff --git a/docs/embed-mode/_index.md b/docs/embed-mode/_index.md index c63111f3285..bc3c42155a2 100644 --- a/docs/embed-mode/_index.md +++ b/docs/embed-mode/_index.md @@ -14,7 +14,7 @@ The ownCloud Web can be consumed by another application in a stripped down versi ## Getting started -To integrate ownCloud Web into your application, add an iframe element pointing to your ownCloud Web deployed instance with additional query parameter `mode=embed`. +To integrate ownCloud Web into your application, add an iframe element pointing to your ownCloud Web deployed instance with additional query parameter `embed=true`. ```html @@ -41,7 +41,7 @@ To maintain uniformity and ease of handling, each event encapsulates the same st ### Example ```html - + ``` + +## Delegate authentication + +If you already have a valid `access_token` that can be used to call the API from within the Embed mode and do not want to force the user to authenticate again, you can delegate the authentication. Delegating authentication will disable internal login form in ownCloud Web and will instead use events to obtain the token and update it. + +### Configuration + +To allow authentication delegation, you need to set a config option `options.embed.delegateAuthentication` to `true`. This can be achieved via query parameter `embed-delegate-authentication=true`. Because we are using `postMessage` method to communicate across different origins, it is best practice to verify that the event originated in known origin and not from some malicious site. To allow this check, you need to set config option `options.embed.delegateAuthenticationOrigin` by adding query parameter `embed-delegate-authentication-origin=my-origin`. The value of this parameter will be compared against the `MessageEvent.origin` value and if they do not match, the token will not be saved. + +### Events + +#### Opening Embed mode + +As we already mentioned, we're using `postMessage` method to allow communication between the Embed mode and parent application. When Embed mode is opened for the first time, user gets redirected to `/web-oidc-callback` page where a message with payload `{ name: 'owncloud-embed:request-token', data: undefined }` is sent to request the `access_token` from the parent application. The parent application should set an event listener before opening the Embed mode and once received, it should send a message with payload `{ name: 'owncloud-embed:refresh-token', data: { access_token: '' } }`. Once Embed mode receives this message, it will save the token in the store and will automatically authenticate the user. + +{{< hint info >}} +To save unnecessary duplication of messages with only different names, the name in the message payload above is exactly the same as when refreshing the token. +{{< /hint >}} + +#### Refreshing token + +When authentication is delegated, the automatic renewal of token inside of ownCloud Web is disabled. In order to refresh the token, a listener is created which awaits a message with payload `{ name: 'owncloud-embed:refresh-token', data: { access_token: '' } }`. Token will then be automatically replaced inside of the Embed mode. diff --git a/packages/web-pkg/src/composables/embedMode/useEmbedMode.ts b/packages/web-pkg/src/composables/embedMode/useEmbedMode.ts index e1fd3ee0f14..f59e8755519 100644 --- a/packages/web-pkg/src/composables/embedMode/useEmbedMode.ts +++ b/packages/web-pkg/src/composables/embedMode/useEmbedMode.ts @@ -14,6 +14,14 @@ export const useEmbedMode = () => { () => store.getters.configuration.options.embed?.messagesOrigin ) + const isDelegatingAuthentication = computed( + () => isEnabled.value && store.getters.configuration.options.embed.delegateAuthentication + ) + + const delegateAuthenticationOrigin = computed( + () => store.getters.configuration.options.embed.delegateAuthenticationOrigin + ) + const postMessage = (name: string, data?: Payload): void => { const options: WindowPostMessageOptions = {} @@ -24,5 +32,20 @@ export const useEmbedMode = () => { window.parent.postMessage({ name, data }, options) } - return { isEnabled, isLocationPicker, messagesTargetOrigin, postMessage } + const verifyDelegatedAuthenticationOrigin = (eventOrigin: string): boolean => { + if (!delegateAuthenticationOrigin.value) { + return true + } + + return delegateAuthenticationOrigin.value === eventOrigin + } + + return { + isEnabled, + isLocationPicker, + messagesTargetOrigin, + isDelegatingAuthentication, + postMessage, + verifyDelegatedAuthenticationOrigin + } } diff --git a/packages/web-pkg/src/configuration/manager.ts b/packages/web-pkg/src/configuration/manager.ts index dc1cb574758..bedb921b922 100644 --- a/packages/web-pkg/src/configuration/manager.ts +++ b/packages/web-pkg/src/configuration/manager.ts @@ -118,6 +118,24 @@ export class ConfigurationManager { set(this.optionsConfiguration, 'isRunningOnEos', get(options, 'isRunningOnEos', false)) set(this.optionsConfiguration, 'ocm.openRemotely', get(options, 'ocm.openRemotely', false)) + + set(this.optionsConfiguration, 'embed.enabled', get(options, 'embed.enabled', false)) + set(this.optionsConfiguration, 'embed.target', get(options, 'embed.target', 'resources')) + set( + this.optionsConfiguration, + 'embed.messagesOrigin', + get(options, 'embed.messagesOrigin', null) + ) + set( + this.optionsConfiguration, + 'embed.delegateAuthentication', + get(options, 'embed.delegateAuthentication', false) + ) + set( + this.optionsConfiguration, + 'embed.delegateAuthenticationOrigin', + get(options, 'embed.delegateAuthenticationOrigin', null) + ) } get options(): OptionsConfiguration { diff --git a/packages/web-pkg/src/configuration/types.ts b/packages/web-pkg/src/configuration/types.ts index 27c242412de..32ad878af5f 100644 --- a/packages/web-pkg/src/configuration/types.ts +++ b/packages/web-pkg/src/configuration/types.ts @@ -36,7 +36,9 @@ export interface OptionsConfiguration { embed?: { enabled?: boolean target?: string - messagesOrigin?: string + messagesOrigin?: string | null + delegateAuthentication?: boolean + delegateAuthenticationOrigin?: string | null } } diff --git a/packages/web-runtime/src/container/bootstrap.ts b/packages/web-runtime/src/container/bootstrap.ts index af7b6f6a160..ec239ea57cb 100644 --- a/packages/web-runtime/src/container/bootstrap.ts +++ b/packages/web-runtime/src/container/bootstrap.ts @@ -36,6 +36,37 @@ import { Resource } from '@ownclouders/web-client' import PQueue from 'p-queue' import { extractNodeId, extractStorageId } from '@ownclouders/web-client/src/helpers' +const getEmbedConfigFromQuery = ( + doesEmbedEnabledOptionExists: boolean +): RawConfig['options']['embed'] => { + const config: RawConfig['options']['embed'] = {} + + if (!doesEmbedEnabledOptionExists) { + config.enabled = getQueryParam('embed') === 'true' + } + + // Can enable location picker in embed mode + const embedTarget = getQueryParam('embed-target') + + if (embedTarget) { + config.target = embedTarget + } + + const delegateAuthentication = getQueryParam('embed-delegate-authentication') + + if (delegateAuthentication) { + config.delegateAuthentication = delegateAuthentication === 'true' + } + + const delegateAuthenticationOrigin = getQueryParam('embed-delegate-authentication-origin') + + if (delegateAuthentication) { + config.delegateAuthenticationOrigin = delegateAuthenticationOrigin + } + + return config +} + /** * fetch runtime configuration, this step is optional, all later steps can use a static * configuration object as well @@ -55,21 +86,14 @@ export const announceConfiguration = async (path: string): Promise