::si ::sa ::su ::so

The ::si, ::sa, ::su and ::so suffixes sign a visitor in, sign them in as another account, read the signed-in user and sign them out. Append one to any URL of a deployed host. Related forms, marked Experimental, run the same actions in a popup, show more details about the user, or sign in from a cross-site embed.

Syntax

plain
https://name.rt.ht/path::si
https://name.rt.ht/path::sa
https://name.rt.ht/path::su
https://name.rt.ht/path::so

Append the suffix to the path of any URL on a deployed host. The text before the suffix is the URL the visitor returns to. Related forms Experimental:

plain
https://name.rt.ht/path::sit
https://name.rt.ht/path::sud
https://name.rt.ht/path::sic   ::sac   ::sitc   ::soc   ::suc
https://name.rt.ht/path::sh

Behavior

::si

Signs the visitor in. The response is a small HTML page whose script sends the browser to the RTCode.io sign-in page. It is not an HTTP redirect. The sign-in page shows the provider menu (GitHub, Google, Microsoft and Apple) and the name of the host. After sign-in, the browser returns to the URL without the suffix.

If the visitor is already signed in to RTCode.io, no provider interaction is needed.

::sa

Signs in as another account. It runs the same flow, but the provider picker always appears. The visitor's RTCode.io session in the editor is neither deleted nor replaced.

::su

Returns the signed-in email of this host as a JSON string, or null. Read it with fetch('::su'). The path in front of the suffix does not matter, because identity is per host.

::so

Signs the visitor out of this host and redirects to the URL without the suffix. Other hosts and the RTCode.io editor stay signed in.

::sit Experimental

Sign in through. If the request already carries a verified session, it redirects straight back to the URL without the suffix. Otherwise it behaves like ::si. RTEdge.net sends anonymous visitors here from the * index, from the ::kv viewer, from the source gate (?) and from ::u and ::t.

::sud Experimental

Returns the user details as pretty-printed JSON: { "u": …, "i": … }. u is the email, and i is { "p": "<provider>" } with the provider GitHub, Google, Microsoft or Apple. Both are null when signed out. The playground does not emulate it.

The popup forms. They run the action in a popup window, post { rt: { user } } to the popup's same-origin opener with postMessage, and close, so the opener never navigates.

::suc posts the current user at once. ::sic, ::sac and ::sitc sign in, then post. ::soc signs out and posts null. The order of the letters does not matter: ::sict is ::sitc.

::sh Experimental

An ES module for signing in from a cross-site embed. Import it, call register(), and use the object it returns: open('i') signs in, open('a') signs in as another account and open('o') signs out. Any other kind throws Error('Invalid auth action'). open(kind, popup) opens the popup, or navigates a popup you opened first, and returns the window.

Importing the module adds a hidden helper frame to your page. After sign-in, the helper posts { type: 'rt.auth.private.v1', action: 'complete', id, n } to the importing page. The module answers GET requests only.

Return URL

The return URL is the request URL without the trailing ::s… suffix, with its query and hash kept. https://new.rt.ht/docs/::si?x=1 returns to /docs/?x=1, followed by the hash the visitor had. A bare trailing ? is kept too.

Reserved text

Any path that contains ::s is answered by the sign-in handler and never reaches your files or your worker. The handler chooses the action from the letters after the last ::s: /x::sorted signs the visitor out, /x::summary returns the user, and unknown letters return a 501 page. Keep ::s out of route, file and worker paths.

Response

SuffixStatusContent-TypeBody or targetCache-Control
::si200text/html;charset=utf-8A page that sends the browser to the sign-in page.no-store
::sa200text/html;charset=utf-8The same page, with the provider picker forced.no-store
::su200application/json;charset=utf-8A JSON string such as "name@example.com", or null.None on RTEdge.net. The playground sends no-store.
::so303NoneLocation: the URL without the suffix, query kept.no-store
::sit303, or 200None, or text/html;charset=utf-8A redirect when already signed in, else the ::si page.no-store
::sud200JSONPretty-printed { "u": …, "i": … }.None
::suc and the other popup forms200HTMLA page whose script posts the user to the opener and closes.None
::sh200text/javascript;charset=utf-8The ES module.no-store
::s with unknown letters501HTMLAn error page.None

Access

Anyone can use the suffixes, signed in or not. The platform answers them before a deployment's access rules apply, so sign-in works on pages that edge:user protects: the 401 page links to ::si, and the 403 page links to ::sa. See Security and access model.

Examples

Sign-in buttons

html
<button id=signin>Sign in</button>
<button id=signas>Switch account</button>
<button id=signout>Sign out</button>
<script type=module>
  signin.onclick = () => location.pathname += '::si'
  signas.onclick = () => location.pathname += '::sa'
  signout.onclick = () => location.pathname += '::so'
</script>

Appending to location.pathname keeps the current path, query and hash, so each action returns to the same page. A plain relative link (href=::si) resolves against the current directory and drops the last path segment, the query and the hash.

Read the signed-in user

html
<output id=who></output>
<script type=module>
  const user = await fetch('::su').then(r => r.json()) // string email or null
  who.value = user ?? 'signed out'
</script>

The same code works in the playground, where ::su answers with the account you are signed in with in the editor.

Sign in with a popup

html
<button id=login>Sign in</button>
<output id=me></output>
<script type=module>
  const show = user => me.value = user ?? 'signed out'
  addEventListener('message', e => { if (e.origin === location.origin && e.data?.rt) show(e.data.rt.user) })
  login.onclick = () => open(location.pathname + '::sic') // popup signs in, posts { rt: { user } }, closes
  show(await fetch('::su').then(r => r.json()))
</script>

The popup forms Experimental work on deployed hosts only. If the visitor is already signed in to RTCode.io, the popup returns at once. If the visitor abandons it, nothing is posted.

Sign in from a cross-site embed

html
<button id=join>Sign in</button>
<output id=info></output>
<script type=module>
  const { register } = await import(new URL('::sh', location.href)) // appends the hidden helper frame
  const auth = register()
  join.onclick = () => auth.open('i') // 'a' = sign in as, 'o' = sign out
  addEventListener('message', async e => {
    if (e.origin === location.origin && e.data?.type === 'rt.auth.private.v1' && e.data.action === 'complete')
      info.value = await fetch('::su').then(r => r.json()) ?? 'signed out'
  })
</script>

::sh Experimental may change. The completion message comes from the helper frame, whose origin is the auth host, here the page's own origin.

Specifications

Not part of any specification. The sign-in suffixes are an RTEdge.net feature. They use these standards:

Specification
HTTP Semantics (RFC 9110), for the 303 and 501 responses
Window.postMessage(), for the popup forms

Availability

FeaturePlaygroundRTEdge.netNotes
::si, ::sa, ::soNoYesNot emulated in the playground. Sign in or out in the editor instead.
::suYesYesThe playground answers with the account you are signed in with in the editor, or null.
::sit, ::sud ExperimentalNoYesNot emulated in the playground.
Popup forms ExperimentalNoYes::sic, ::sac, ::sitc, ::soc and ::suc.
::sh ExperimentalNoYesAn ES module for cross-site embeds.

See also