Skip to content

feat(ocm): implement WAYF page and enhance invitation workflow#13243

Open
MahdiBaghbani wants to merge 1 commit into
owncloud:masterfrom
MahdiBaghbani:ocis/feature/ocm-wayf
Open

feat(ocm): implement WAYF page and enhance invitation workflow#13243
MahdiBaghbani wants to merge 1 commit into
owncloud:masterfrom
MahdiBaghbani:ocis/feature/ocm-wayf

Conversation

@MahdiBaghbani

Copy link
Copy Markdown

Although this PR is referenced under cs3org/OCM-STA#1, it is not directly part of the STA funded work. However, since the changes are closely related and beneficial to OCM, I decided to port them here as well for consistency.

This PR depends on this PR owncloud/reva#432 to be merged in Reva.
This PR depends on this PR owncloud/ocis#11758 to be merged in oCIS.

Description

This PR implements the frontend components for WAYF (Where Are You From) functionality in the OCM web application, enabling users to discover and select their cloud provider when accepting federated invitations. The implementation provides a user friendly interface for OCM provider selection and complements the backend WAYF discovery endpoints.

Technical

Things I've added to the web application:

  • WAYF Page: Provider selection interface with federation browsing and manual provider entry
  • WAYF is public page: Public access to WAYF page without authentication requirement
  • Invitation Acceptance Modal: Component for invitation acceptance workflow from other EFSS WAYF Pages
  • Token Management: Multiple copy formats (plain, base64, WAYF link) for outgoing invitations
  • Self-Domain Prevention: Prevent users from selecting their own instance
  • Shared Composables: Reusable logic for invitation acceptance and WAYF functionality
  • Federation list display with automatic grouping
  • Manual provider domain entry with OCM discovery

Code Changes

  • New Components:

    • src/views/Wayf.vue: Main WAYF page with provider selection UI, search functionality, and federation display
    • src/views/InvitationAcceptanceModal.vue: Modal component for accepting invitations with provider validation
  • New Composables:

    • src/composables/useWayf.ts: Core WAYF logic including federation loading, provider discovery, and self-domain validation
    • src/composables/useInvitationAcceptance.ts: Shared invitation acceptance logic with self-generated token detection
  • New Types:

    • src/types/wayf.ts: TypeScript interfaces for federation and provider data structures
  • Updated Routing (src/index.ts):

    • Added /wayf route with authContext: 'anonymous' for public access
    • Added /accept-invite route for invitation acceptance workflow
    • Included authentication context documentation
  • Modified Views:

    • src/views/OutgoingInvitations.vue: Added three token copy options (plain, base64, WAYF link) with visual improvements
    • src/views/IncomingInvitations.vue: Refactored to use shared useInvitationAcceptance composable
    • src/views/ConnectionsPanel.vue: Minor UI adjustments
    • src/views/App.vue: Updated to support new routes
  • Store Updates (web-pkg/src/composables/piniaStores/inviteTokensList.ts):

    • Extended token objects with tokenAtProvider and wayfLink properties
  • Documentation:

    • docs/Where Are You From page.md: WAYF usage guide, API endpoints, and testing scenarios

Videos of the changes and general flow of the OCM app

From oCIS to Nextcloud

Ocis-To-Nextcloud.webm

From Nextcloud to oCIS

Nextcloud-to-oCIS.webm

@update-docs

update-docs Bot commented Oct 27, 2025

Copy link
Copy Markdown

Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a changelog item based on your changes.

@CLAassistant

CLAassistant commented Oct 27, 2025

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
20.0% Condition Coverage on New Code (required ≥ 50%)

See analysis details on SonarQube Cloud

@MahdiBaghbani MahdiBaghbani marked this pull request as draft October 28, 2025 13:14
add: Wayf.vue component with provider selection and federation discovery
add: useWayf.ts composable for WAYF logic and provider management
add: InvitationAcceptanceModal.vue for invitation acceptance
add: useInvitationAcceptance.ts composable to share acceptance logic
add: wayf.ts types for federation and provider interfaces
add: WAYF documentation
add: /wayf route with anonymous authentication support
add: /accept-invite route for invitation acceptance workflow
add: Configure anonymous authContext for public WAYF access
refactor: IncomingInvitations to use shared acceptance composable
add: multiple token copy options to OutgoingInvitations (plain, base64, WAYF link)
enhance: token display with separate copy buttons for each format
enhance: Prevent users from accepting self-generated invitations
enhance: Filter out current instance from federation provider lists
enhance: Validate manual provider entries against self-domain

Signed-off-by: Mahdi Baghbani <mahdi-baghbani@azadehafzar.io>
@MahdiBaghbani MahdiBaghbani marked this pull request as ready for review November 18, 2025 11:45

@DeepDiver1975 DeepDiver1975 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainer review (automated, by Claude Code on behalf of @DeepDiver1975). Thanks for porting this WAYF work over — the UX flow is clean and the self-domain prevention is a nice touch. Since this is a federation/invitation flow that crosses a trust boundary, I focused most of the review on security. A few things should be addressed before merge.

Security (please address)

1. Open-redirect / javascript: URI risk in WAYF navigation (useWayf.ts).
navigateToProvider() does window.location.href = finalUrl, where inviteAcceptDialog for an absolute URL is used as-is with no scheme/host validation:

let inviteDialogUrl = provider.inviteAcceptDialog
if (inviteDialogUrl.startsWith('/')) { ... }
// If it's already absolute, use as-is   <-- no validation
const finalUrl = buildProviderUrl(inviteDialogUrl, token, providerDomain)
window.location.href = finalUrl

provider.inviteAcceptDialog comes from the /sciencemesh/federations response (server.inviteAcceptDialog). If a federation registry entry can contain a value like javascript:... or data:..., or an arbitrary attacker-controlled host, you get a redirect-to-attacker (and the invite token — a bearer secret — is appended to that URL). Please validate before navigating:

  • Require https: (reject anything that isn't http/https); explicitly reject javascript:/data:/blob:.
  • For absolute URLs, consider pinning the host to provider.fqdn so the dialog URL can't redirect the token to a different host than the provider the user selected.
    buildProviderUrl already constructs via new URL(), which throws on a malformed base — but javascript:https://... parses fine, so new URL() alone is not sufficient protection here.

2. Invite token in URL query string. The WAYF link (?token=...) and the redirect to the foreign provider both carry the raw token in the URL. Tokens in URLs land in browser history, server logs, and the Referer header. This may be inherent to the OCM/WAYF handoff spec, but please confirm it's intended and that the token is single-use / short-lived on the backend.

3. discoverProvider trusts the backend inviteAcceptDialog likewise — same validation point as (1) applies to the discovery response path (manual entry).

Correctness / quality

  • loadFederations()new URL(server.url).hostname can throw. If any server.url in the federations response is malformed, the .map() throws and the whole list fails to render (caught by the outer try → generic "Failed to load providers"). Consider per-server try/catch so one bad entry doesn't blank the page.
  • useInvitationAcceptance.acceptInvitation has an unused response (const response = await ...; return true) — drop the variable or return the response.
  • isSelfDomain matches only on bare hostname. http://example.com and https://example.com:443 are normalized (scheme + port stripped) — good — but a path/userinfo (example.com/foo, user@example.com) wouldn't normalize. Minor, since manual entry then flows to discovery, but worth tightening.
  • App.vue accept-invite watch reads route.query.token/providerDomain but the modal then calls httpAuthenticated.post('/sciencemesh/accept-invite'). The /accept-invite route has no explicit authContext, so it defaults to user — an anonymous user arriving from a foreign WAYF page will hit auth before the modal is useful. Confirm that's the intended UX (login-then-accept) and that the token/provider survive the auth redirect.

i18n

Looks good — all user-facing strings use $gettext, including pluralization for provider counts. Two existing typos are fixed ("conections"→"connections", "Invition"→"Invitation"), nice.

Tests

web-app-ocm currently ships no unit tests, and this PR (≈1300 LOC of new composables/components) adds none. The pure logic in useWayf.ts (isSelfDomain, filterProviders, buildProviderUrl) and useInvitationAcceptance.ts (isOwnGeneratedToken, token decode in IncomingInvitations.decodeInviteToken) is very testable and is exactly the security-sensitive surface — please add unit tests, especially covering the open-redirect validation once added and the base64/plain token decode edge cases.

Changelog

Present and well-formed (changelog/unreleased/enhancement-ocm-wayf-page.md), correct Enhancement: type and PR URL. No action needed.

Minor

  • docs/Where Are You From page.md is a manual test plan rather than user/dev docs — fine to keep, but consider moving the test scenarios into actual specs.
  • Several console.error calls log full error objects; ensure tokens aren't logged in those error payloads.

Overall: solid feature, but the WAYF redirect target needs scheme/host validation before this should merge, given a bearer token rides along on that redirect. Happy to re-review once the validation + a couple of tests are in.


This review was generated by an automated agent. Note: depends on owncloud/reva#432 and owncloud/ocis#11758 per the PR description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants