gitoriaLog in with ident

tracker

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit3691e1763691e176tracker#1: empty tracker with the ident login (state of 2026-09-27)mre3691e176/project.hl

4.7 KB

  1. // project.hl — tracker.worldapi.org: STEP 1 (tracker.worldapi.org#1), an empty shell.
  2. // Login exactly like calendar/notes: ident only, no own passwords (README "How apps use
  3. // ident" of ident.worldapi.org). No shows, no data yet — later steps come from the creator
  4. // (CONCEPT.md).
  5. //
  6. // GET /login -> <ident>/login?key=<key>&return=<this app>/callback
  7. // /callback?ident_code=<code> the app's server exchanges the code for the identity's
  8. // short id (POST <ident>/api/exchange) and signs this
  9. // app's OWN session in (session.user = { identity })
  10. // POST /logout signs this app's session out (ident's own session,
  11. // and the browser's ident cookie, are untouched)
  12. //
  13. // Config (env; no committed secret — the first deploy registers this app in ident and sets
  14. // these, done by the architect, as with ident's own .env for SMTP):
  15. // TRACKER_PORT (45008), TRACKER_URL (this app's own origin, for the return URL),
  16. // IDENT_URL (https://ident.worldapi.org), TRACKER_KEY (pk_…), TRACKER_SECRET (sk_…)
  17. import WebFramework from 'hl:web'
  18. import { Response } from 'hl:http1'
  19. import { fetch } from 'hl:fetch'
  20. import { env } from 'hl:proc'
  21. import Styles from './styles.hl'
  22. import Home from './components/home.hl'
  23. static appTitle = "tracker"
  24. styles = Styles
  25. port = env('TRACKER_PORT') != null ? toNumber(env('TRACKER_PORT')) : 45008
  26. selfUrl = env('TRACKER_URL') != null ? env('TRACKER_URL') : 'http://127.0.0.1:' + port
  27. identUrl = env('IDENT_URL') != null ? env('IDENT_URL') : 'https://ident.worldapi.org'
  28. identKey = env('TRACKER_KEY') != null ? env('TRACKER_KEY') : ''
  29. identSecret = env('TRACKER_SECRET') != null ? env('TRACKER_SECRET') : ''
  30. escape = (s) => { return ('' + s).replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;') }
  31. errorPage = (status, message) => {
  32. let html = '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>tracker</title></head><body style="margin:0;padding:2rem;font:16px/1.5 system-ui,sans-serif;color:rgb(195,200,205);background:rgb(25,30,35)"><p style="color:#f44747">' + escape(message) + '</p><p><a id="home" href="/" style="color:#4ec9b0">start page</a></p></body></html>'
  33. return new Response(html, { status = status headers = { 'Content-Type' = 'text/html; charset=utf-8' 'Cache-Control' = 'no-store' } })
  34. }
  35. redirect = (url) => { return new Response('', { status = 302 headers = { 'Location' = url 'Cache-Control' = 'no-store' } }) }
  36. // ---- GET /login: the login button --------------------------------------------------------
  37. login = (route, req) => {
  38. if (req.method != 'GET') { return errorPage(405, 'Use a GET request.') }
  39. if (identKey == '') { return errorPage(500, 'This app is not registered with ident yet (TRACKER_KEY/TRACKER_SECRET missing).') }
  40. let href = identUrl + '/login?key=' + identKey + '&return=' + encodeURIComponent(selfUrl + '/callback')
  41. return redirect(href)
  42. }
  43. // ---- /callback?ident_code=<code>: exchange it for the identity's short id ----------------
  44. callback = (route, req) => {
  45. let q = req.query != null ? req.query : {}
  46. let code = q.ident_code
  47. if (code == null || code == '') { return errorPage(400, 'no ident_code on the callback.') }
  48. let r = fetch(identUrl + '/api/exchange', { method = 'POST' json = { key = identKey secret = identSecret code = code } timeoutMs = 5000 })
  49. let j = r.json()
  50. if (r.status != 200 || j == null || j.identity == null) {
  51. return errorPage(400, 'sign-in failed (' + r.status + '): ' + (j != null && j.error != null ? j.error : r.text()))
  52. }
  53. let s = req.session
  54. if (s == null) { return errorPage(500, 'no session — reload and try again.') }
  55. s.user = { identity = j.identity }
  56. return redirect('/')
  57. }
  58. // ---- POST /logout: ends THIS app's session; ident's own login is untouched --------------
  59. logout = (route, req) => {
  60. if (req.method != 'POST') { return redirect('/') }
  61. let s = req.session
  62. if (s != null) { s.user = null }
  63. return redirect('/')
  64. }
  65. routes = [
  66. { pattern = "/favicon.ico" direct = "" }
  67. { pattern = "/login" function = login }
  68. { pattern = "/callback" function = callback }
  69. { pattern = "/logout" function = logout }
  70. { pattern = "/" component = Home }
  71. ]
  72. // cookies are per host, not per port: an own name keeps this app's session apart from
  73. // ident's own (and from any other worldapi app sharing a dev host), see ident README "Design
  74. // tokens" / hybriel#10 hybriel#17.
  75. sessionCookie = 'trackersid'
  76. watching = env('TRACKER_WATCH') != '0'
  77. server = new WebFramework(routes = routes, styles = styles, port = port, sessionCookie = sessionCookie, watchMode = watching)

Branches

Latest commits

  • 3691e176tracker#1: empty tracker with the ident login (state of 2026-09-27)mre