MishkaCmsCoreWeb.ReturnTo (Mishka CMS Core v0.0.1)

Copy Markdown View Source

Where a sign-in goes back to, as a token this application signed.

A page that sends a visitor to /auth with the address to come back to puts that address in the URL, and it then travels through the sign-in and register form actions and through the sign_in_with_token handoff before MishkaCmsCoreWeb.AuthController redirects to it. Everything on that road is writable by whoever is walking it, so a return_to taken at its word would make /auth the front door of a phishing site.

Nothing here parses what arrives. sign/2 takes a path the router itself matched — built with ~p, never assembled from a parameter — and returns a Phoenix.Token; verify/2 hands that exact path back, and hands back nothing for a token that was tampered with, signed with another secret or salt, signed too long ago, or never a token at all. A plain path is never a token, so ?return_to=/en/members and ?return_to=https://evil.example are refused alike, and the caller sends whoever cannot be verified home.

Summary

Types

Whatever names the endpoint holding the secret: a conn, a socket, or the endpoint itself

Functions

Signs a same-site path into the opaque token that travels as return_to.

Gives back the path a token was signed for, or nil when it cannot be verified.

Types

context()

@type context() :: Plug.Conn.t() | Phoenix.LiveView.Socket.t() | module()

Whatever names the endpoint holding the secret: a conn, a socket, or the endpoint itself

Functions

sign(context, path)

@spec sign(context(), term()) :: String.t() | nil

Signs a same-site path into the opaque token that travels as return_to.

Same-site is exactly what Phoenix.Controller.redirect/2 will hand out: a single leading /, and none of the characters a browser's URL parser strips or reinterprets or a response header cannot carry. That is a rule about what a browser does with an address, not about how the address reads, so an escaped /%2F/elsewhere.example is a page of this site and is signed as one — nothing on the way to the browser turns that escape into another origin.

Returns nil for anything else, so the sign-in it starts simply has nowhere to come back to.

verify(context, token)

@spec verify(context(), term()) :: String.t() | nil

Gives back the path a token was signed for, or nil when it cannot be verified.