Your Canvas calendar feed URL is a credential

Treat your Canvas calendar feed URL like a password. Somewhere in that URL is an opaque per-user token — on Canvas it sits in the path itself, as the user_... segment of .../feeds/calendars/user_....ics; some other schools' and services' feeds put it in a query parameter instead — and that token is the part that identifies you. In building Margin's feed handling we treat the whole feed URL, path and query alike, as identity-bearing for exactly this reason. Students paste these URLs into forums, help-desk tickets, shared documents, and group chats all the time without realizing it. Never post yours anywhere public.

What this looks like from a student's side

Nothing. That is the whole problem with it.

A feed URL does not look like a secret. It is a long, ugly, machine-shaped string that reads like plumbing rather than like a password, and the place you meet it is a "copy this link" box next to instructions for setting up your calendar. Most setup flows never ask you to protect it. When the calendar subscription fails to work, the natural next move is to paste the link into a question — on Reddit, in a campus Discord, in a ticket to IT, into a support chat with a third-party app — so that whoever is helping can see what is wrong.

In Margin's feed handling, the URL is all a fetch needs: there is no login step in the request we make, and nothing else in that request establishes who is asking. So anyone holding the link can make the same request and get the same answer: your class schedule — which courses you are enrolled in, what is due, and when. And unlike a password you can change after an awkward moment, a link that has been indexed or screenshotted is very hard to take back.

Why the URL works this way

A calendar subscription is a plain, unauthenticated fetch. Your calendar app is not signed in to Canvas and cannot be asked to log in — it requests a URL on a schedule and expects an .ics file back. So the credential has to travel inside the URL itself. That constraint is inherent to the format, not a Canvas choice; where the token sits in the URL is the only part that varies.

The consequence is worth stating plainly: possession is authorization. Anything that ends up holding the URL — a browser history, a link preview, a chat log, a pasted screenshot, an app you gave it to — holds the ability to read that feed, for as long as the token stays valid.

Two extra edges are worth knowing:

What you can do about it

Evidence