RenewlyContract renewal management (Renewly home)
Writing
Security
All writing20 May 20267 min read

What we hardened in auth and privacy this week

Passkeys, a required second step before you can turn off two-factor authentication, GDPR cookie consent, a broader data export, and audit logs across auth, billing, account, and team routes. A plain-language recap of what changed and why.

Vendor contracts contain sensitive commercial terms. Pricing, notice periods, counterparty names, payment schedules. When you upload one to Renewly, you are trusting us with that information. This past week we shipped a focused batch of auth and privacy changes. No new features - just closing gaps we had identified in how accounts are secured, how data is scoped between tenants, and how GDPR obligations are met.

Passkeys are now available

You can now register a passkey from your account security settings. Touch ID, Face ID, or a hardware security key such as a YubiKey or Titan Key - any WebAuthn-compatible authenticator works. You can register more than one passkey, give each a name, and revoke individual passkeys without touching the others.

Passkeys are phishing-resistant by construction. A passkey is cryptographically bound to renewly.gg. It cannot be used on a lookalike domain. It cannot be extracted and replayed the way a one-time code from an app can be. This is the standard the industry is moving toward and we wanted it available to anyone who holds a vendor contract portfolio on the platform.

Magic links with rate limiting by intent

Renewly has always been passwordless - there is no stored password on your account to leak or brute-force. This week we completed work ensuring every magic-link request runs through a server-side rate limiter with separate budgets per intent: 10 attempts per 15 minutes for login, 5 for signup, 3 for password reset.

Previously the browser could call the auth provider directly and bypass the edge limiter entirely. The server-side route closes that gap. The auth provider's own per-email rate limit still applies as a second layer.

Magic link redirects are now validated against a trusted origin allowlist. A link generated for the production domain cannot be redirected to an external host via a manipulated Host header.

Turning off two-factor authentication now needs a second step

Two-factor authentication (the six-digit codes from an authenticator app) has been available in Renewly for a while. There was a gap: turning it off ran in the browser without requiring you to prove your identity again first.

The risk: if an attacker gained access to an active session - stolen cookie, compromised device - they could turn off two-factor authentication without ever knowing your six-digit code, then change the email address or export contract data.

The fix moves the disable action to the server and requires you to complete a second authentication step before it proceeds - the same step you'd use to sign in with two-factor authentication. An attacker who has only hijacked your session, and not that second step, cannot turn off your two-factor authentication.

Org data does not bleed into personal views

Renewly supports both solo accounts and team workspaces. The rule: once a user joins an organisation, they see only that organisation's contracts. No personal contracts and no other organisations' data appear in the same view.

The original dashboard code showed personal contracts alongside org contracts for org members, inflating counts and leaking context across the boundary. We fixed it across six surfaces: the dashboard, contracts list, calendar, iCal export, CSV export, and the top-nav search.

Analytics queries are now scoped through a per-session contract ID whitelist. A member of one organisation cannot cause the analytics endpoint to return aggregate data from a different tenant, even with a crafted request.

Workspace switching is validated server-side. Attempting to switch to an organisation you are not a member of is rejected at the API layer, not just hidden in the client UI.

Audit logging extended across auth, billing, and team routes

We had audit logging on contract operations and billing events. We extended it this week to cover:

  • Authentication events: login, logout, MFA enable and disable, passkey registration and revocation, session revocation
  • Account events: profile updates, data exports, deletion requests and cancellations
  • Settings changes: notification preferences, cookie preferences
  • Team events: invitations sent, declined and accepted, role changes
  • Vendor actions: opt-outs, opt-back-ins, alert acknowledgements and dismissals
  • Workspace switches

Audit logs are retained for 3 years. Your own audit log is included in any data export you request.

Non-essential trackers are now consent-gated

Until this week, analytics and session recording loaded on first visit without asking. That was not compliant with GDPR Article 7, which requires freely given, informed, and specific consent before non-essential tracking can begin.

The fix: a consent banner on first visit. Third-party analytics trackers do not load unless you accept. Crisp live chat is not gated because it is a functional support service, not a marketing tool.

You can change your choice at any time from Settings - Account - Cookie Preferences. That is the GDPR Article 7(3) affordance: consent must be as easy to withdraw as it was to give.

Data export now covers 16 categories

The GDPR Article 15 data export now covers 16 categories: profile, account, contracts and extracted data, tags, contract-tag associations, notifications, preferences, audit logs, account deletion requests, inbox aliases, inbox messages, vendor signal alerts, vendor signal opt-outs, user sessions, calendar integrations, and webhook endpoints.

Credential material - OAuth tokens, webhook secrets - is redacted from the export. You get the configuration data but not the secrets themselves. The export is available from Settings at any time with no support request required.

Stripe billing is now tied to your account, not just email

Stripe customer records now carry your Renewly account's unique ID, set at checkout creation. Every billing notification from Stripe is checked against that ID first. The practical effect: a billing event for one customer cannot be applied to a different account, even if they share an email address. The check happens on Stripe's side and is verified again on every notification.

What this adds up to

Vendor contracts sit at the intersection of commercial sensitivity and operational dependency. You cannot cancel a vendor because you missed the notice window. You cannot renegotiate terms you cannot find. If the contract data leaks before a renewal, you have lost negotiating leverage before the conversation starts.

Getting the security baseline right is not a feature. It is the minimum for something this close to your vendor relationships. The full breakdown is on the security page.

Never miss a vendor renewal

Upload a vendor contract and get every renewal date, notice window, and auto-renewal clause extracted in seconds. Free for 5 contracts.

Start free
Filed under Security · 20 May 2026All writing
Matt du Jardin

Founder of Renewly. Over a decade in IT operations and vendor management across financial services and technology. LinkedIn