Repository navigation
Conversation
Deploying matrix-authentication-service-docs with
|
| Latest commit: |
1a7956a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d90b457f.matrix-authentication-service-docs.pages.dev |
| Branch Preview URL: | https://devonh-invite-guests.matrix-authentication-service-docs.pages.dev |
This was referenced Oct 5, 2026
devonh
force-pushed
the
devonh/invite-guests
branch
from
October 9, 2026 20:10
9036a44 to
ba74383
Compare
sandhose
force-pushed
the
devonh/invite-guests
branch
from
October 13, 2026 09:32
46adaf5 to
0e6125c
Compare
sandhose
force-pushed
the
devonh/invite-guests
branch
from
October 13, 2026 12:02
0e6125c to
957df9a
Compare
sandhose
force-pushed
the
devonh/invite-guests
branch
from
October 13, 2026 13:35
957df9a to
4ff9aba
Compare
Contributor
|
A build for this PR at commit a2424d8 has been created through the Z-Build-Workflow label by sandhose. Docker image is available at:
Pre-built binaries are available through the workflow run artifacts. |
devonh
changed the base branch from
devonh/open-room
to
devonh/username-templates
October 14, 2026 22:01
Contributor
|
A build for this PR at commit 4b7d820 has been created through the Z-Build-Workflow label by sandhose. Docker image is available at:
Pre-built binaries are available through the workflow run artifacts. |
sandhose
force-pushed
the
devonh/invite-guests
branch
from
October 16, 2026 09:29
4034706 to
7fb5563
Compare
`SendGuestInviteEmailJob` sends one guest invite email for a registration token, so that a bad address or a transient SMTP failure retries that one email rather than a whole batch. It takes both the recipient and the invite link from the token, so the link can't be sent to anyone but the address the token is pinned to. A token revoked, expired or used by the time the job runs gets no email. The link registers with the token, then opens the room through the `OpenRoom` post-auth action. A browser already signed in to MAS goes straight to the room. The email names the room and who sent the invite, when the job has them, and tells the recipient they are asking to join, since someone in the room still has to approve them. Each combination has a message of its own, rather than one message passed into another, which would escape the names twice in HTML. Its subject names neither, as both are chosen by the sender. Mints a single-use, passwordless registration token for each guest, pinned to their address and username and expiring after `guest_invites.invite_lifetime`, and schedules their invite emails. The links are only ever mailed to the guests, so the response carries a count rather than the tokens. Everything is validated before anything is minted, and the batch is committed as a whole, so a rejected request sends no email at all. Each invite goes through the registration policy as `POST /register` will check it, so an invite the guest form would refuse is a 400 naming the invite and every violation. The policy sees an empty requester, as the request comes from the inviter's homeserver rather than the guest. A username that is already taken is refused up front, rather than emailed out as a link nobody can register with. Addresses are deduplicated ignoring case before the usernames are checked, keeping the first entry, so a repeated address never counts as a username given twice. The caller sends the room name and the inviter on the inviter's behalf, so the endpoint doesn't trust them. The room name and the inviter's display name are stripped of control characters, including the bidirectional controls `char::is_control` misses, and truncated before they are stored in the job payload. The inviter's Matrix ID is never truncated, so it can't show a server it isn't from: it is kept whole if it is valid under the historical grammar, and left out otherwise. The room ID is checked as `/open-room` checks it, and is only recorded on the span once it is valid. The endpoint answers 404 while guest invites are disabled. The check is in the handler, as the admin router doesn't see the configuration. `guest_invites.enabled` turns it on, and requires `client_room_url`. `/open-room` keeps using `client_room_url` while the endpoint is disabled, so that links already sent keep working. `invite_lifetime` is a positive number of seconds that fits in a `u32`, so MAS refuses a zero or negative lifetime at startup, and adding it to the current time can't overflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R27QDzJTWSBxkrfbxBxcKF
Nothing in the endpoint, its email or its config is specific to who is invited, so they take neutral names: `POST /api/admin/v1/room-invites` with the `room-invite` OpenAPI tag, `SendRoomInviteEmailJob` on the `send-room-invite-email` queue, the `emails/room_invite.*` templates and their `mas.emails.room_invite.*` strings, and the `room_invites_enabled` and `room_invite_lifetime` site config fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RkxQWj8RAKd7iZ1JQtXdxm
The room invites endpoint stores each invite's room on the registration token it mints, so the email job no longer carries the room, and the email links to `/register?token=…`, built as the admin API's `invite_url` is. Registering, or opening the link again once signed in, reaches the room through the token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RkxQWj8RAKd7iZ1JQtXdxm
An invite's `username` may be a template with one `{random}`, which the
minted token stores as is and registering fills in. The endpoint checks
a template by filling `{random}` with letters and digits and running the
registration policy on the result, so a template the policy refuses is a
400 when inviting rather than an invite that can't register. A template
with more than one `{random}` is a 400. Every invite may carry the same
template, and the name it fills in to is only known when registering, so
the repeated-username and username-taken checks apply to literal
usernames only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RkxQWj8RAKd7iZ1JQtXdxm
The token admin API can now mint a token that opens a room, as the `room-invites` endpoint's tokens do, and shows the room on every token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lkm4JwQcd72UvZuWVLMg2e
A pinned email must parse and a username template must have one
`{random}`. Both must pass the registration policy, and a pinned
username which isn't a template must not be taken, so that a token
isn't minted that nobody can register with.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R27QDzJTWSBxkrfbxBxcKF
`POST /api/admin/v1/room-invites` minted a token for each address and emailed it in one call. It becomes `POST /api/admin/v1/user-registration-tokens/invite-emails`, which emails the links of tokens minted through the token API: to the token's own address, or to one given for a token without an address. A token without a room can be sent too. The `room_invites.enabled` and `room_invites.invite_lifetime` settings are gone: minting is the token API's, which takes an expiry, and every admin API client may already read invite links. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R27QDzJTWSBxkrfbxBxcKF
The room invite email becomes the invite email, as it is sent for any registration token. When the token opens no room, it invites the recipient to the server, names no room and asks for no approval. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R27QDzJTWSBxkrfbxBxcKF
devonh
force-pushed
the
devonh/invite-guests
branch
from
October 16, 2026 19:01
7fb5563 to
484924f
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0145EoXnCPYo2bnhuGTohX55
Adding a registration token renders a username template with a sample
`random`, in strict mode, and checks the result against the registration
policy. A template that doesn't parse or render is a 400 carrying the
render error. This replaces the `{random}` sample check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145EoXnCPYo2bnhuGTohX55
devonh
added a commit
to element-hq/element-modules
that referenced
this pull request
Oct 28, 2026
MAS now takes a registration token's username as a minijinja template, with
`random` as its variable. The old `{random}` placeholder is refused when
minting a token by a MAS with
element-hq/matrix-authentication-service#6026's template check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145EoXnCPYo2bnhuGTohX55
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TODO:
This description is still LLM generated. I will personally reword it before marking this PR as ready for review.
Lets an admin API client email the invite link of a registration token. It's built for the restricted-guests Synapse module, whose
invite_guestsendpoint (element-hq/element-modules#304) checks the inviter and the room, mints one token per invitee, then has MAS email them all.Replaces #6003.
Minting:
POST /api/admin/v1/user-registration-tokensIt now takes a
room_id, and checks what a token pins before minting it:The module mints each invite as:
Sending:
POST /api/admin/v1/user-registration-tokens/invite-emailsurn:mas:admin), as for every admin endpoint.emailmay be given, but must match it, ignoring case. A token without one needsemail, so a multi-use token can be sent to several addresses.room_nameis only given if every token has a room. A token and address given more than once, ignoring case, are sent once. The batch commits as a whole.The link is
/register?token=…. For a token with a room, the email names the room and the inviter, and after signing up, or signing in with an existing account, the invitee is sent to the room in their client, where they ask to join. For a token without a room, the email invites them to the server.Every guest invite is an invite registration: its form offers no upstream providers, and it skips the registration rate limit.
There is no configuration: the caller sets each token's expiry, and
room_invites.client_room_url(#6025) is still where invitees are sent.Notes for review
POST /registerchecks the homeserver when the guest signs up.invite_url, so the client that mints a token can read its link. Emailing it through MAS doesn't change who can see it.