Skip to main content

Invite activation URL — field migration guide (plan-155 → plan-156)

What this guide covers

Plan 155 (PR svc-access#98) fixed bug D1: the activation_url field in invitation responses was only populated when a devMode flag was set, leaving production operators without a usable link. The fix made activation_url unconditionally populated and introduced a legacy alias _dev_link for backward compatibility.

This guide describes the current transition state and the steps to complete it in plan-156.


Current state (post–plan-155)

Send invite — POST /access/admin/tenants/{id}/invite

The backend (svc-access) returns both fields, populated identically:

{
"invitation": {},
"activation_url": "https://access.eigenoid.com/activate?token=abc123",
"_dev_link": "https://access.eigenoid.com/activate?token=abc123"
}
json
FieldStatus
activation_urlCanonical — new name since plan-155. Migrate reads here.
_dev_linkLegacy alias — kept for app-access-admin backward compat. Remove after frontend migrates.

app-access-admin (src/lib/services/invitations.ts) currently reads _dev_link via the legacyInviteField indirection constant. This is intentional until plan-156 updates the reads.

Regenerate invite — POST /access/admin/tenants/{id}/users/{uid}/invite/regenerate

Returns a separate DTO — no migration needed:

{
"invite_url": "https://access.eigenoid.com/activate?token=xyz789",
"expires_at": "2026-08-01T12:00:00Z"
}
json

invite_url is the stable field for this endpoint. It is not the same as _dev_link or activation_url — it comes from a different response type (InviteRegenerateResult).


Bug D1 — what was fixed

Before plan-155, InvitationService.Create and RegenerateToken accepted a devMode bool parameter. When devMode was false (the production default), neither _dev_link nor activation_url was populated in the response. Admins attempting to copy an invite link in production received an empty field.

Plan-155 removed the devMode gate and removed config.IsDevLinkVisible(). The activation URL is now always constructed via internal/urlutil.BuildActivationURL(PUBLIC_BASE_URL, token) and returned unconditionally.


Plan-156 completion checklist

Complete these steps to finish the migration and remove the legacy field:

1. Update app-access-admin

In src/lib/services/invitations.ts:

- const legacyInviteField = ["_dev", "link"].join("_");

return {
invitation: raw.invitation as Invitation,
- invite_url: raw[legacyInviteField] as string | undefined,
+ invite_url: raw["activation_url"] as string | undefined,
};
diff

Update the InvitationResult JSDoc and remove the legacyInviteField constant.

2. Remove DevLinkLegacy from svc-access

In internal/service/invitation_service.go:

type InvitationResult struct {
Invitation *model.Invitation `json:"invitation"`
ActivationURL string `json:"activation_url"`
- // DevLinkLegacy mirrors ActivationURL under the legacy field name "_dev_link"
- // TODO(plan-156): remove after frontend migrates to activation_url.
- DevLinkLegacy string `json:"_dev_link,omitempty"`
}
diff

Also remove the DevLinkLegacy population in Create and RegenerateToken.

# In app-access-admin repo — should return zero results after step 1:
grep -r "_dev_link\|legacyInviteField\|dev.*link" src/
bash

4. Smoke-test in dev

  1. Create a new invitation via the admin portal → copy the activation URL from the success dialog.
  2. Open the activation URL in an incognito browser tab — the enrollment flow should start.
  3. Regenerate an invite for a pending_invite user → confirm the URL in the dialog is valid.

Cache-Control: no-store

Both invite endpoints set Cache-Control: no-store. This header is intentional — activation tokens are single-use credentials and must not be stored by shared caches or CDN edges. Do not remove it.


References

  • svc-access PR: eigenoid/svc-access#98
  • svc-access CHANGELOG entry: Plan 155
  • Source: internal/urlutil/activation.goBuildActivationURL
  • Source: internal/service/invitation_service.goInvitationResult
  • Source: app-access-admin/src/lib/services/invitations.tslegacyInviteField