Skip to content

v0.4.2 — Activities Calendar, iCal Feeds, SSO & Magic-Link Login

Choose a tag to compare

@benjaminbowles benjaminbowles released this 16 Jun 12:21

Added

Calendar

  • Full-page Calendar at /calendar that visualises scheduled activities, gated on the existing activities.access permission — no new entity, table, or permission
  • Three views — Day (staff columns against a vertical hour-axis), Week (seven day-columns against an hour-axis, coloured by owner; the default view), and Month (a seven-column grid of day-cells with event chips)
  • Pixel-precise hour-axis placement in Day and Week views; the visible hour window auto-expands to include any event outside working hours so nothing is clipped
  • All-day handling computed from the activity's times (starts at 00:00 with no end or ending at 23:59/next midnight), rendered in a band above the grid and as month chips
  • Pickable start date defaulting to today and looking forwards, with Today / Previous / Next navigation that steps by the active view
  • Owner filter restricting the calendar to selected staff users, with deterministic per-user colours shared across columns, blocks, chips, and avatars
  • Owner and participant avatars on each event (owner first, then participants), showing each user's photo where available and coloured initials otherwise
  • Unscheduled tray listing activities with no start time, each opening its detail modal so it can be scheduled
  • View, date, and filter state persisted in the URL so a calendar link reopens exactly as shared

Modals & Realtime

  • Add modal opened from an empty slot (pre-filled owner and start time) or a month day-cell (pre-filled date), reusing the existing activity form
  • Detail modal showing an activity's subject, type, status, owner, time, and location, with Complete, Edit, and Delete actions and a link to the full /activities/{id} page
  • Edit modal using the same form as create, reached from the detail modal
  • All writes flow through the existing CreateActivity / UpdateActivity / CompleteActivity / DeleteActivity actions — same validation, authorisation, audit logging, and activity.* webhooks — and the calendar refreshes instantly and locally after every change

iCal Feeds

  • Secure global iCal feed at /calendar/feed.ics (every scheduled activity) and per-user feeds at /calendar/feed/{user}.ics (a single owner's activities)
  • Feeds secured with Laravel signed URLs (no expiry); a missing or tampered signature returns 403, and the feeds serve without an authenticated session
  • Feed window covers activities from one year ago with no forward bound; activities without a start time are excluded
  • Hand-rolled RFC 5545 output (no new Composer dependency) — one VEVENT per activity with UID, DTSTAMP, DTSTART/DTEND in UTC, SUMMARY, LOCATION, DESCRIPTION, STATUS, TRANSP, and ORGANIZER, with proper text escaping and 75-octet line folding; all-day events emit ;VALUE=DATE
  • Copyable subscribe URLs surfaced in a feed modal on the calendar page (global and own feed for every user; full per-user list for administrators)

Settings

  • New Settings → Calendar page at /settings/calendar showing the current user's personal feed: a subscribe URL with a copy button, a Download .ics button, and Google/Apple/Outlook subscription help; administrators also see the global feed URL

Navigation

  • Calendar opened from the top navigation bar's calendar icon (gated on activities.access), with Activities relocated into the CRM menu; a "Go to Calendar" command-palette navigation entry is also available

Documentation

  • Platform documentation page for the Calendar (views, navigation, owner filter, modals, unscheduled tray, the settings page, and the week-start / working-hours / weekend settings that affect it)
  • API documentation page for the calendar feed endpoints, covering the signed-URL security model and its global-only revocation tradeoff

Single Sign-On (SSO)

  • SSO login via Google and Microsoft 365 (Azure common tenant) powered by Laravel Socialite — staff can sign in with their Google or Microsoft account without a Signals password
  • Auto-link by verified email: on first SSO login the provider's verified email is matched to an existing, active Signals user and the identity is linked automatically; unknown or unverified emails are denied and the user must be invited before using SSO
  • Subsequent logins match by stored provider identity (robust to later email changes in Signals)
  • Self-hosted credential configuration in Settings → Integrations → Single Sign-On: per-provider enable toggle, Client ID, and Client Secret (stored encrypted at rest; write-only — not re-displayed after save), with the correct callback URL shown for copy-paste into the Google Cloud Console or Azure portal
  • Signals Cloud mode: credential fields are hidden in the Integrations UI; only enable/disable toggles are shown (credentials are managed centrally by Signals Cloud)
  • Per-role SSO enforcement in Settings → Security → SSO Enforcement: admins can require specific roles to use SSO; password login for enforced roles is blocked with an inline guidance message and SSO buttons; the Owner role is always exempt (break-glass — cannot be selected)
  • 2FA still applies: SSO replaces only the password step; users with 2FA enabled are redirected to the existing one-time-code challenge after a successful OAuth callback
  • Allowed email domains allow-list (Settings → Integrations → Single Sign-On): when set, only users whose IdP email domain is on the list may sign in or auto-link — enforced on both the auto-link and existing-link paths — strongly recommended with Microsoft's multitenant common setting to prevent cross-tenant account takeover; an empty list permits any domain
  • Client secrets are now write-only end-to-end: they are never loaded back into the Integrations form, never rendered to the browser, and a blank secret field on save keeps the existing stored secret (enter a new value to rotate)
  • Routes: GET /auth/google/redirect, GET /auth/google/callback, GET /auth/microsoft/redirect, GET /auth/microsoft/callback; unknown providers return 404
  • oauth_identities table and OAuthIdentity model recording the (provider, provider_id, user_id, email) link

Authentication — Password Manager & Accessibility

  • One-time-code 2FA input now auto-submits when six digits are entered, removing the need to press Enter or click a button
  • /.well-known/change-password well-known URL pointing to the profile password-change page, so browsers and password managers can locate the change-password flow automatically
  • Invitation acceptance page includes a username field pre-filled with the invited email, giving password managers the anchor they need to save the credential against the right account on first use
  • Login and password forms now carry canonical autocomplete="username" / autocomplete="current-password" / autocomplete="new-password" attributes throughout the auth flow for consistent password-manager integration

Documentation

  • Platform documentation page for Single Sign-On — covers enabling Google and Microsoft (self-hosted and Signals Cloud), callback URL registration, auto-link matching, per-role enforcement, and 2FA interaction

Magic-Link Login

  • Passwordless login for existing users via a single-use link emailed to their address — an opt-in alternative to password login, enabled in Settings → Security → Magic-Link Login (off by default)
  • Links are valid for 15 minutes and can only be consumed once; requesting a new link invalidates any prior unconsumed link for that account
  • 2FA is not bypassed — users with 2FA enabled (or enforced) are redirected to the existing one-time-code challenge after the link is consumed
  • Anti-enumeration: Signals always returns the same neutral response ("if an account exists, we've emailed a link") regardless of whether the email is registered, so no information about account existence is leaked; the email is dispatched asynchronously so response timing cannot be used to infer an account's existence
  • Blocked for SSO-enforced roles at both the request step and the consume step, so a policy change after issue invalidates the outstanding link; the Owner role is always exempt
  • Tokens are stored as a SHA-256 hash only — the plaintext travels only in the email and is compared with hash_equals; request and consume endpoints are rate-limited per email and per IP
  • Platform documentation page for Magic-Link Login — covers the login flow, enabling the feature, anti-enumeration behaviour, SSO interaction, and the security model

Changed

Activities

  • Activity type is now a user-editable list of values — the seeded "Activity Type" list (Task, Call, Fax, Email, Meeting, Note, Letter), manageable in Settings → List Names — replacing the previous fixed enum. The change applies across the Activities pages, the Calendar create/edit/detail modals, and the API, where type_id now references a list_values id and responses include activity_type_name
  • Activity participants now resolve to the linked user's name and avatar (with photo where available) on the activity page, in the calendar, and in the owner/participant pickers; the activity page lists the owner first followed by participants
  • Activities API operations now publish full OpenAPI request/response schemas — the paginated list envelope, the single-resource envelope, and the create/update request bodies

Notes

  • The calendar reuses the existing Activities write path and activities.* permissions; no new permissions, webhook events, or database tables were introduced
  • Feed URLs are not individually revocable — rotating the application's APP_KEY invalidates all existing feed URLs at once

Also in this release: the 12-module scaffolding audit & remediation, UAT fixes, the Phase-2 Definition of Done, and the Phase-2 completion review — correctness, security, and quality fixes across the v0.4.x work.

Full Changelog: v0.4.1...v0.4.2