Collection of themes/skins for the Fossil SCM

โŒˆโŒ‹ โއ branch:  Fossil Skins Extra


Hex Artifact Content

Artifact 3f08e34f95af9d2bc2db426dc85118957c333748:

  • Executable file extroot/auth — part of check-in [ab76ada131] at 2021-04-06 11:52:30 on branch trunk — Update comments on user.* table. (user: mario size: 12682)

0000: 23 21 2f 75 73 72 2f 62 69 6e 2f 70 68 70 2d 63  #!/usr/bin/php-c
0010: 67 69 20 2d 64 63 67 69 2e 66 6f 72 63 65 5f 72  gi -dcgi.force_r
0020: 65 64 69 72 65 63 74 3d 30 0a 3c 3f 70 68 70 0a  edirect=0.<?php.
0030: 23 20 65 6e 63 6f 64 69 6e 67 3a 20 75 74 66 2d  # encoding: utf-
0040: 38 0a 23 20 61 70 69 3a 20 63 67 69 0a 23 20 74  8.# api: cgi.# t
0050: 79 70 65 3a 20 61 75 74 68 0a 23 20 74 69 74 6c  ype: auth.# titl
0060: 65 3a 20 49 6e 64 69 65 41 75 74 68 20 61 75 74  e: IndieAuth aut
0070: 68 6f 72 69 7a 61 74 69 6f 6e 5f 65 6e 64 70 6f  horization_endpo
0080: 69 6e 74 0a 23 20 64 65 73 63 72 69 70 74 69 6f  int.# descriptio
0090: 6e 3a 20 56 65 72 69 66 69 65 73 20 61 20 72 65  n: Verifies a re
00a0: 6d 6f 74 65 20 75 73 65 72 20 69 64 20 28 55 52  mote user id (UR
00b0: 4c 29 20 61 67 61 69 6e 73 74 20 61 63 74 69 76  L) against activ
00c0: 65 20 66 6f 73 73 69 6c 20 6c 6f 67 69 6e 0a 23  e fossil login.#
00d0: 20 76 65 72 73 69 6f 6e 3a 20 30 2e 35 0a 23 20   version: 0.5.# 
00e0: 73 74 61 74 65 3a 20 61 6c 70 68 61 0a 23 20 64  state: alpha.# d
00f0: 65 70 65 6e 64 73 3a 20 70 68 70 3a 63 75 72 6c  epends: php:curl
0100: 2c 20 70 68 70 3a 73 71 6c 69 74 65 0a 23 20 64  , php:sqlite.# d
0110: 6f 63 3a 20 68 74 74 70 73 3a 2f 2f 77 77 77 2e  oc: https://www.
0120: 77 33 2e 6f 72 67 2f 54 52 2f 69 6e 64 69 65 61  w3.org/TR/indiea
0130: 75 74 68 2f 23 61 75 74 68 65 6e 74 69 63 61 74  uth/#authenticat
0140: 69 6f 6e 2c 0a 23 20 20 20 20 20 68 74 74 70 73  ion,.#     https
0150: 3a 2f 2f 69 6e 64 69 65 77 65 62 2e 6f 72 67 2f  ://indieweb.org/
0160: 61 75 74 68 6f 72 69 7a 61 74 69 6f 6e 2d 65 6e  authorization-en
0170: 64 70 6f 69 6e 74 0a 23 20 63 6f 6e 66 69 67 3a  dpoint.# config:
0180: 20 2d 0a 23 0a 23 20 4d 69 6e 69 6d 61 6c 20 69   -.#.# Minimal i
0190: 6d 70 6c 65 6d 65 6e 74 61 74 69 6f 6e 20 6f 66  mplementation of
01a0: 20 49 6e 64 69 65 41 75 74 68 20 6c 6f 67 69 6e   IndieAuth login
01b0: 20 65 6e 64 70 6f 69 6e 74 2e 20 52 75 6e 73 20   endpoint. Runs 
01c0: 61 73 20 66 6f 73 73 69 6c 20 63 67 69 0a 23 20  as fossil cgi.# 
01d0: 65 78 74 65 6e 73 69 6f 6e 20 74 6f 20 76 65 72  extension to ver
01e0: 69 66 79 20 63 75 72 72 65 6e 74 6c 79 20 6c 6f  ify currently lo
01f0: 67 67 65 64 20 69 6e 20 75 73 65 72 2e 20 43 6f  gged in user. Co
0200: 6e 66 69 72 6d 73 20 49 6e 64 69 65 41 75 74 68  nfirms IndieAuth
0210: 2f 4f 41 75 74 68 0a 23 20 72 65 71 75 65 73 74  /OAuth.# request
0220: 2c 20 61 6e 64 20 6b 65 65 70 73 20 6c 6f 67 69  , and keeps logi
0230: 6e 20 74 6f 6b 65 6e 73 20 69 6e 20 60 66 78 5f  n tokens in `fx_
0240: 61 75 74 68 60 20 74 61 62 6c 65 2e 0a 23 0a 23  auth` table..#.#
0250: 20 54 68 69 73 20 6d 69 67 68 74 20 70 72 6f 67   This might prog
0260: 72 65 73 73 20 69 6e 74 6f 20 61 20 2f 74 6f 6b  ress into a /tok
0270: 65 6e 20 65 6e 64 70 6f 69 6e 74 2c 20 69 64 65  en endpoint, ide
0280: 61 6c 6c 79 20 65 6e 61 62 6c 69 6e 67 20 2f 6d  ally enabling /m
0290: 69 63 72 6f 70 75 62 0a 23 20 66 6f 72 20 74 68  icropub.# for th
02a0: 65 20 74 69 63 6b 65 74 20 73 79 73 74 65 6d 20  e ticket system 
02b0: 6c 61 74 65 72 20 6f 6e 2e 20 57 68 69 63 68 20  later on. Which 
02c0: 69 73 20 77 68 79 20 74 68 65 20 66 78 5f 61 75  is why the fx_au
02d0: 74 68 2f 74 6f 6b 65 6e 20 74 61 62 6c 65 0a 23  th/token table.#
02e0: 20 69 73 20 73 6f 6d 65 77 68 61 74 20 6f 66 20   is somewhat of 
02f0: 61 20 62 6c 6f 62 20 63 6f 6c 6c 65 63 74 69 6f  a blob collectio
0300: 6e 20 69 6e 20 69 74 73 65 6c 66 2e 0a 23 0a 23  n in itself..#.#
0310: 0a 23 20 23 23 20 53 45 54 55 50 0a 23 0a 23 20  .# ## SETUP.#.# 
0320: 53 6f 20 74 68 69 73 20 72 65 71 75 69 72 65 73  So this requires
0330: 20 69 6e 73 74 61 6c 6c 61 74 69 6f 6e 20 69 6e   installation in
0340: 20 66 6f 73 73 69 6c 73 20 65 78 74 72 6f 6f 74   fossils extroot
0350: 3a 20 28 69 6e 74 65 72 6e 61 6c 20 63 67 69 20  : (internal cgi 
0360: 73 63 72 69 70 74 73 29 2c 0a 23 20 61 6e 64 20  scripts),.# and 
0370: 6d 69 67 68 74 20 6e 6f 74 20 77 6f 72 6b 20 69  might not work i
0380: 6e 20 63 68 72 6f 6f 74 20 6a 61 69 6c 73 20 28  n chroot jails (
0390: 64 75 65 20 74 6f 20 70 68 70 2d 63 67 69 20 73  due to php-cgi s
03a0: 68 65 62 61 6e 67 29 2e 20 43 6f 70 79 20 74 68  hebang). Copy th
03b0: 65 20 73 63 72 69 70 74 0a 23 20 69 6e 74 6f 20  e script.# into 
03c0: 65 78 74 2f 2c 20 70 72 65 66 65 72 72 61 62 6c  ext/, preferrabl
03d0: 79 20 77 69 74 68 6f 75 74 20 2e 70 68 70 20 6f  y without .php o
03e0: 72 20 2e 63 67 69 20 65 78 74 65 6e 73 69 6f 6e  r .cgi extension
03f0: 2c 20 61 6e 64 20 63 68 6d 6f 64 20 2b 78 20 69  , and chmod +x i
0400: 74 2e 0a 23 0a 23 20 49 66 20 69 6e 76 6f 6b 65  t..#.# If invoke
0410: 64 20 77 69 74 68 6f 75 74 20 61 6e 79 20 70 61  d without any pa
0420: 72 61 6d 65 74 65 72 73 2c 20 74 68 69 73 20 66  rameters, this f
0430: 6f 73 73 69 6c 2d 63 67 69 20 73 63 72 69 70 74  ossil-cgi script
0440: 20 77 69 6c 6c 20 69 6e 69 74 69 61 6c 69 7a 65   will initialize
0450: 0a 23 20 74 68 65 20 60 66 78 5f 61 75 74 68 60  .# the `fx_auth`
0460: 20 64 61 74 61 62 61 73 65 20 74 61 62 6c 65 2e   database table.
0470: 0a 23 0a 23 20 55 73 65 72 20 61 63 63 6f 75 6e  .#.# User accoun
0480: 74 73 20 61 72 65 20 72 65 71 75 69 72 65 64 20  ts are required 
0490: 74 6f 20 6c 69 73 74 20 68 6f 6d 65 70 61 67 65  to list homepage
04a0: 73 20 69 6e 20 74 68 65 20 60 69 6e 66 6f 60 20  s in the `info` 
04b0: 63 6f 6c 75 6d 6e 2e 20 45 61 63 68 0a 23 20 6e  column. Each.# n
04c0: 65 65 64 73 20 61 74 20 6c 65 61 73 74 20 6f 6e  eeds at least on
04d0: 65 2c 20 73 6f 20 72 65 71 75 65 73 74 73 20 63  e, so requests c
04e0: 61 6e 27 74 20 62 65 20 75 73 65 64 20 74 6f 20  an't be used to 
04f0: 61 70 70 72 6f 76 65 20 61 72 62 69 74 72 61 72  approve arbitrar
0500: 79 20 75 72 6c 73 2e 0a 23 0a 23 20 41 66 74 65  y urls..#.# Afte
0510: 72 77 61 72 64 73 20 63 6f 6e 66 69 67 75 72 65  rwards configure
0520: 20 79 6f 75 72 20 68 6f 6d 65 70 61 67 65 20 74   your homepage t
0530: 6f 20 61 6c 6c 6f 77 20 69 74 73 20 75 73 65 20  o allow its use 
0540: 61 73 20 49 6e 64 69 65 41 75 74 68 20 69 64 3a  as IndieAuth id:
0550: 0a 23 20 3c 6c 69 6e 6b 20 72 65 6c 3d 61 75 74  .# <link rel=aut
0560: 68 6f 72 69 7a 61 74 69 6f 6e 5f 65 6e 64 70 6f  horization_endpo
0570: 69 6e 74 20 68 72 65 66 3d 22 68 74 74 70 3a 2f  int href="http:/
0580: 2f 66 6f 73 73 69 6c 2e 64 6f 6d 61 69 6e 2f 65  /fossil.domain/e
0590: 78 74 2f 61 75 74 68 2e 63 67 69 22 3e 0a 23 0a  xt/auth.cgi">.#.
05a0: 23 0a 23 20 23 23 20 54 4f 4b 45 4e 0a 23 0a 23  #.# ## TOKEN.#.#
05b0: 20 54 68 65 20 66 78 5f 61 75 74 68 20 74 61 62   The fx_auth tab
05c0: 6c 65 20 63 6f 6e 74 61 69 6e 73 20 69 6e 64 69  le contains indi
05d0: 76 69 64 75 61 6c 20 63 6f 6c 75 6d 6e 73 20 6e  vidual columns n
05e0: 6f 77 20 74 6f 20 72 65 63 6f 72 64 20 70 72 65  ow to record pre
05f0: 76 69 6f 75 73 0a 23 20 61 75 74 68 6f 72 69 7a  vious.# authoriz
0600: 61 74 69 6f 6e 20 72 65 71 75 65 73 74 73 2e 0a  ation requests..
0610: 23 20 4d 6f 73 74 20 6f 66 20 77 68 69 63 68 20  # Most of which 
0620: 69 73 20 75 6e 6e 65 63 65 73 73 61 72 79 20 74  is unnecessary t
0630: 6f 20 6b 65 65 70 20 61 66 74 65 72 20 74 68 65  o keep after the
0640: 20 69 6e 69 74 69 61 6c 20 72 65 71 75 65 73 74   initial request
0650: 2e 20 54 68 69 73 20 69 73 0a 23 20 6c 61 72 67  . This is.# larg
0660: 65 6c 79 20 66 6f 72 20 64 65 62 75 67 67 69 6e  ely for debuggin
0670: 67 2e 20 49 66 20 2f 74 6f 6b 65 6e 20 73 75 70  g. If /token sup
0680: 70 6f 72 74 20 67 65 74 73 20 69 6d 70 6c 65 6d  port gets implem
0690: 65 6e 74 65 64 2c 20 69 74 20 6d 69 67 68 74 20  ented, it might 
06a0: 65 69 74 68 65 72 0a 23 20 63 72 65 61 74 65 20  either.# create 
06b0: 64 69 73 74 69 6e 63 74 20 63 6f 64 65 20 65 6e  distinct code en
06c0: 74 72 69 65 73 2c 20 6f 72 20 6a 75 73 74 20 61  tries, or just a
06d0: 64 64 20 22 73 63 6f 70 65 22 3a 20 65 74 63 2e  dd "scope": etc.
06e0: 20 74 6f 20 74 68 65 20 65 78 69 73 74 69 6e 67   to the existing
06f0: 0a 23 20 65 6e 74 72 69 65 73 20 2d 20 61 6e 64  .# entries - and
0700: 20 72 65 75 73 65 20 74 68 65 20 61 75 74 68 20   reuse the auth 
0710: 6b 65 79 73 20 61 73 20 4f 41 75 74 68 20 42 65  keys as OAuth Be
0720: 61 72 65 72 20 74 6f 6b 65 6e 2e 0a 23 0a 23 0a  arer token..#.#.
0730: 23 20 23 23 20 50 52 4f 54 4f 43 4f 4c 0a 23 0a  # ## PROTOCOL.#.
0740: 23 20 41 75 74 68 6f 72 69 7a 61 74 69 6f 6e 20  # Authorization 
0750: 72 65 71 75 65 73 74 3a 0a 23 20 20 20 20 3f 6d  request:.#    ?m
0760: 65 3d 68 74 74 70 73 3a 2f 2f 75 73 65 72 2e 65  e=https://user.e
0770: 78 61 6d 70 6c 65 2e 6f 72 67 2f 0a 23 20 20 20  xample.org/.#   
0780: 20 26 63 6c 69 65 6e 74 5f 69 64 3d 68 74 74 70   &client_id=http
0790: 3a 2f 2f 61 70 70 2e 65 78 61 6d 70 6c 65 2e 63  ://app.example.c
07a0: 6f 6d 2f 0a 23 20 20 20 20 26 72 65 64 69 72 65  om/.#    &redire
07b0: 63 74 5f 75 72 69 3d 68 74 74 70 3a 2f 2f 61 70  ct_uri=http://ap
07c0: 70 2e 65 78 61 6d 70 6c 65 2e 63 6f 6d 2f 6c 6f  p.example.com/lo
07d0: 67 69 6e 2f 63 61 6c 6c 62 61 63 6b 0a 23 20 20  gin/callback.#  
07e0: 20 20 26 73 74 61 74 65 3d 31 32 33 34 35 36 37    &state=1234567
07f0: 38 39 30 0a 23 20 20 20 20 26 72 65 73 70 6f 6e  890.#    &respon
0800: 73 65 5f 74 79 70 65 3d 63 6f 64 65 0a 23 20 20  se_type=code.#  
0810: 20 20 26 73 63 6f 70 65 3d 70 72 6f 66 69 6c 65    &scope=profile
0820: 2b 63 72 65 61 74 65 2b 75 70 64 61 74 65 2b 64  +create+update+d
0830: 65 6c 65 74 65 0a 23 20 20 20 20 26 63 6f 64 65  elete.#    &code
0840: 5f 63 68 61 6c 6c 65 6e 67 65 3d 42 73 65 36 34  _challenge=Bse64
0850: 78 31 32 33 2e 2e 0a 23 20 20 20 20 26 63 6f 64  x123...#    &cod
0860: 65 5f 63 68 61 6c 6c 65 6e 67 65 5f 6d 65 74 68  e_challenge_meth
0870: 6f 64 3d 53 32 35 36 0a 23 20 41 63 74 69 6f 6e  od=S256.# Action
0880: 3a 0a 23 20 20 20 20 c2 b7 20 76 65 72 69 66 79  :.#    ยท verify
0890: 20 75 73 65 72 20 65 78 69 73 74 73 2c 20 69 73   user exists, is
08a0: 20 6c 6f 67 67 65 64 20 69 6e 2c 20 61 6e 64 20   logged in, and 
08b0: 6d 65 3d 20 70 61 72 61 6d 65 74 65 72 20 69 73  me= parameter is
08c0: 20 77 68 69 74 65 6c 69 73 74 65 64 0a 23 20 20   whitelisted.#  
08d0: 20 20 c2 b7 20 63 6f 6e 66 69 72 6d 20 70 65 72    ยท confirm per
08e0: 20 62 75 74 74 6f 6e 20 70 72 65 73 73 2c 20 6f   button press, o
08f0: 72 20 64 69 72 65 63 74 20 75 6e 61 75 74 68 65  r direct unauthe
0900: 6e 74 69 63 61 74 65 64 20 75 73 65 72 20 74 6f  nticated user to
0910: 20 2f 6c 6f 67 69 6e 20 70 61 67 65 0a 23 20 20   /login page.#  
0920: 20 20 c2 b7 20 67 65 6e 65 72 61 74 65 20 61 6e    ยท generate an
0930: 20 61 72 62 69 74 72 61 72 79 20 63 6f 64 65 2c   arbitrary code,
0940: 20 72 65 63 6f 72 64 20 69 6e 20 66 78 5f 61 75   record in fx_au
0950: 74 68 20 74 61 62 6c 65 2f 6a 73 6f 6e 20 62 6c  th table/json bl
0960: 6f 62 0a 23 20 52 65 73 70 6f 6e 73 65 3a 0a 23  ob.# Response:.#
0970: 20 20 20 20 4c 6f 63 61 74 69 6f 6e 3a 20 68 74      Location: ht
0980: 74 70 73 3a 2f 2f 61 70 70 2e 65 78 61 6d 70 6c  tps://app.exampl
0990: 65 2e 63 6f 6d 2f 6c 6f 67 69 6e 2f 63 61 6c 6c  e.com/login/call
09a0: 62 61 63 6b 3f 63 6f 64 65 3d 2e 2e 2e 26 73 74  back?code=...&st
09b0: 61 74 65 3d 31 32 33 34 35 36 37 38 39 30 0a 23  ate=1234567890.#
09c0: 0a 23 20 56 65 72 69 66 69 63 61 74 69 6f 6e 20  .# Verification 
09d0: 72 65 71 75 65 73 74 3a 0a 23 20 20 20 20 3f 63  request:.#    ?c
09e0: 6f 64 65 3d 24 31 79 24 2e 2e 2e 2e 2e 0a 23 20  ode=$1y$......# 
09f0: 20 20 20 26 63 6c 69 65 6e 74 5f 69 64 3d 68 74     &client_id=ht
0a00: 74 70 3a 2f 2f 61 70 70 2e 65 78 61 6d 70 6c 65  tp://app.example
0a10: 2e 63 6f 6d 2f 0a 23 20 20 20 20 26 72 65 64 69  .com/.#    &redi
0a20: 72 65 63 74 5f 75 72 69 3d 68 74 74 70 3a 2f 2f  rect_uri=http://
0a30: 61 70 70 2e 65 78 61 6d 70 6c 65 2e 63 6f 6d 2f  app.example.com/
0a40: 6c 6f 67 69 6e 2f 63 61 6c 6c 62 61 63 6b 0a 23  login/callback.#
0a50: 20 52 65 73 70 6f 6e 73 65 3a 0a 23 20 20 20 20   Response:.#    
0a60: 7b 20 22 6d 65 22 3a 20 22 68 74 74 70 73 3a 2f  { "me": "https:/
0a70: 2f 75 73 65 72 2e 65 78 61 6d 70 6c 65 2e 6f 72  /user.example.or
0a80: 67 2f 22 20 7d 0a 23 0a 23 0a 0a 69 66 20 28 24  g/" }.#.#..if ($
0a90: 5f 52 45 51 55 45 53 54 5b 22 64 62 67 22 5d 29  _REQUEST["dbg"])
0aa0: 20 7b 0a 20 20 20 20 65 72 72 6f 72 5f 72 65 70   {.    error_rep
0ab0: 6f 72 74 69 6e 67 28 45 5f 41 4c 4c 29 3b 20 69  orting(E_ALL); i
0ac0: 6e 69 5f 73 65 74 28 22 64 69 73 70 6c 61 79 5f  ni_set("display_
0ad0: 65 72 72 6f 72 73 22 2c 20 31 29 3b 0a 7d 0a 0a  errors", 1);.}..
0ae0: 23 2d 2d 20 64 61 74 61 62 61 73 65 20 28 3d 3d  #-- database (==
0af0: 20 66 6f 73 73 69 6c 20 72 65 70 6f 29 0a 66 75   fossil repo).fu
0b00: 6e 63 74 69 6f 6e 20 64 62 28 24 73 71 6c 3d 22  nction db($sql="
0b10: 22 2c 20 24 70 61 72 61 6d 73 3d 5b 5d 29 20 7b  ", $params=[]) {
0b20: 0a 20 20 20 20 73 74 61 74 69 63 20 24 64 62 3b  .    static $db;
0b30: 0a 20 20 20 20 69 66 20 28 65 6d 70 74 79 28 24  .    if (empty($
0b40: 64 62 29 29 20 7b 0a 20 20 20 20 20 20 20 20 24  db)) {.        $
0b50: 64 62 20 3d 20 6e 65 77 20 50 44 4f 28 22 73 71  db = new PDO("sq
0b60: 6c 69 74 65 3a 24 5f 53 45 52 56 45 52 5b 46 4f  lite:$_SERVER[FO
0b70: 53 53 49 4c 5f 52 45 50 4f 53 49 54 4f 52 59 5d  SSIL_REPOSITORY]
0b80: 22 29 3b 0a 20 20 20 20 20 20 20 20 23 24 64 62  ");.        #$db
0b90: 2d 3e 73 65 74 41 74 74 72 69 62 75 74 65 28 50  ->setAttribute(P
0ba0: 44 4f 3a 3a 41 54 54 52 5f 45 52 52 4d 4f 44 45  DO::ATTR_ERRMODE
0bb0: 2c 20 50 44 4f 3a 3a 45 52 52 4d 4f 44 45 5f 57  , PDO::ERRMODE_W
0bc0: 41 52 4e 49 4e 47 29 3b 0a 20 20 20 20 7d 0a 20  ARNING);.    }. 
0bd0: 20 20 20 69 66 20 28 24 70 61 72 61 6d 73 29 20     if ($params) 
0be0: 7b 0a 20 20 20 20 20 20 20 20 24 73 74 6d 74 20  {.        $stmt 
0bf0: 3d 20 24 64 62 2d 3e 70 72 65 70 61 72 65 28 24  = $db->prepare($
0c00: 73 71 6c 29 3b 0a 20 20 20 20 20 20 20 20 24 73  sql);.        $s
0c10: 74 6d 74 2d 3e 65 78 65 63 75 74 65 28 24 70 61  tmt->execute($pa
0c20: 72 61 6d 73 29 3b 0a 20 20 20 20 20 20 20 20 72  rams);.        r
0c30: 65 74 75 72 6e 20 24 73 74 6d 74 2d 3e 66 65 74  eturn $stmt->fet
0c40: 63 68 41 6c 6c 28 29 3b 0a 20 20 20 20 7d 0a 20  chAll();.    }. 
0c50: 20 20 20 65 6c 73 65 20 7b 0a 20 20 20 20 20 20     else {.      
0c60: 20 20 72 65 74 75 72 6e 20 24 64 62 2d 3e 71 75    return $db->qu
0c70: 65 72 79 28 24 73 71 6c 29 3b 0a 20 20 20 20 7d  ery($sql);.    }
0c80: 0a 7d 0a 66 75 6e 63 74 69 6f 6e 20 63 72 65 61  .}.function crea
0c90: 74 65 5f 74 61 62 6c 65 28 29 20 7b 0a 20 20 20  te_table() {.   
0ca0: 20 64 62 28 22 43 52 45 41 54 45 20 54 41 42 4c   db("CREATE TABL
0cb0: 45 20 49 46 20 4e 4f 54 20 45 58 49 53 54 53 20  E IF NOT EXISTS 
0cc0: 60 66 78 5f 61 75 74 68 60 20 28 20 20 2d 2d 20  `fx_auth` (  -- 
0cd0: 49 6e 64 69 65 41 75 74 68 20 74 6f 6b 65 6e 20  IndieAuth token 
0ce0: 74 61 62 6c 65 0a 20 20 20 20 20 20 20 20 60 63  table.        `c
0cf0: 6f 64 65 60 20 54 45 58 54 2c 20 20 20 20 20 20  ode` TEXT,      
0d00: 20 20 20 20 20 20 2d 2d 20 4f 41 75 74 68 20 61        -- OAuth a
0d10: 75 74 68 6f 72 69 7a 61 74 69 6f 6e 20 63 6f 64  uthorization cod
0d20: 65 0a 20 20 20 20 20 20 20 20 60 74 79 70 65 60  e.        `type`
0d30: 20 54 45 58 54 2c 20 20 20 20 20 20 20 20 20 20   TEXT,          
0d40: 20 20 2d 2d 20 6f 6e 65 20 6f 66 20 69 64 2c 63    -- one of id,c
0d50: 6f 64 65 2c 74 6f 6b 65 6e 2c 72 65 76 6f 6b 65  ode,token,revoke
0d60: 64 0a 20 20 20 20 20 20 20 20 60 73 63 6f 70 65  d.        `scope
0d70: 60 20 54 45 58 54 2c 20 20 20 20 20 20 20 20 20  ` TEXT,         
0d80: 20 20 2d 2d 20 70 65 72 6d 69 73 73 69 6f 6e 73    -- permissions
0d90: 20 28 63 72 65 61 74 65 2c 75 70 64 61 74 65 2c   (create,update,
0da0: 64 65 6c 65 74 65 29 0a 20 20 20 20 20 20 20 20  delete).        
0db0: 60 6c 6f 67 69 6e 60 20 54 45 58 54 2c 20 20 20  `login` TEXT,   
0dc0: 20 20 20 20 20 20 20 20 2d 2d 20 66 6f 73 73 69          -- fossi
0dd0: 6c 20 75 73 65 72 20 61 63 63 6f 75 6e 74 0a 20  l user account. 
0de0: 20 20 20 20 20 20 20 60 6d 65 60 20 54 45 58 54         `me` TEXT
0df0: 2c 20 20 20 20 20 20 20 20 20 20 20 20 20 20 2d  ,              -
0e00: 2d 20 68 74 74 70 73 3a 2f 2f 75 73 65 72 77 65  - https://userwe
0e10: 62 69 64 2e 65 78 61 6d 70 6c 65 2e 6f 72 67 2f  bid.example.org/
0e20: 0a 20 20 20 20 20 20 20 20 60 63 6c 69 65 6e 74  .        `client
0e30: 5f 69 64 60 20 54 45 58 54 2c 20 20 20 20 20 20  _id` TEXT,      
0e40: 20 2d 2d 20 68 74 74 70 73 3a 2f 2f 72 65 6d 6f   -- https://remo
0e50: 74 65 61 70 70 2e 65 78 61 6d 70 6c 65 2e 63 6f  teapp.example.co
0e60: 6d 2f 0a 20 20 20 20 20 20 20 20 60 72 65 64 69  m/.        `redi
0e70: 72 65 63 74 5f 75 72 69 60 20 54 45 58 54 2c 20  rect_uri` TEXT, 
0e80: 20 20 20 2d 2d 20 68 74 74 70 73 3a 2f 2f 61 70     -- https://ap
0e90: 70 2f 6c 6f 67 69 6e 2f 63 61 6c 6c 62 61 63 6b  p/login/callback
0ea0: 0a 20 20 20 20 20 20 20 20 60 73 74 61 74 65 60  .        `state`
0eb0: 20 54 45 58 54 2c 20 20 20 20 20 20 20 20 20 20   TEXT,          
0ec0: 20 2d 2d 20 72 65 6d 6f 74 65 20 73 65 73 73 69   -- remote sessi
0ed0: 6f 6e 20 69 64 20 28 31 32 33 34 35 2e 2e 29 0a  on id (12345..).
0ee0: 20 20 20 20 20 20 20 20 60 63 6f 64 65 5f 63 68          `code_ch
0ef0: 61 6c 6c 65 6e 67 65 60 20 54 45 58 54 2c 20 20  allenge` TEXT,  
0f00: 2d 2d 20 70 72 65 2d 68 61 73 68 65 64 20 73 65  -- pre-hashed se
0f10: 63 72 65 74 20 66 6f 72 20 6c 61 74 65 72 20 74  cret for later t
0f20: 6f 6b 65 6e 20 72 65 71 0a 20 20 20 20 20 20 20  oken req.       
0f30: 20 60 63 6f 64 65 5f 63 68 61 6c 6c 65 6e 67 65   `code_challenge
0f40: 5f 6d 60 20 54 45 58 54 2c 2d 2d 20 68 61 73 68  _m` TEXT,-- hash
0f50: 20 6d 65 74 68 6f 64 20 66 6f 72 20 73 65 63 72   method for secr
0f60: 65 74 0a 20 20 20 20 20 20 20 20 60 65 78 70 69  et.        `expi
0f70: 72 65 73 60 20 49 4e 54 20 20 20 20 20 20 20 20  res` INT        
0f80: 20 20 20 2d 2d 20 63 6f 64 65 20 76 61 6c 69 64     -- code valid
0f90: 20 75 6e 74 69 6c 20 74 69 6d 65 73 74 61 6d 70   until timestamp
0fa0: 0a 20 20 20 20 29 22 29 3b 0a 7d 0a 0a 23 2d 2d  .    )");.}..#--
0fb0: 20 66 6f 73 73 69 6c 20 48 54 4d 4c 20 6f 75 74   fossil HTML out
0fc0: 70 75 74 0a 66 75 6e 63 74 69 6f 6e 20 70 61 67  put.function pag
0fd0: 65 5f 68 74 6d 6c 28 24 68 74 6d 6c 29 20 7b 0a  e_html($html) {.
0fe0: 20 20 20 20 68 65 61 64 65 72 28 22 43 6f 6e 74      header("Cont
0ff0: 65 6e 74 2d 54 79 70 65 3a 20 74 65 78 74 2f 68  ent-Type: text/h
1000: 74 6d 6c 3b 20 65 6e 63 6f 64 69 6e 67 3d 75 74  tml; encoding=ut
1010: 66 2d 38 22 29 3b 0a 20 20 20 20 24 73 76 67 20  f-8");.    $svg 
1020: 3d 20 3c 3c 3c 53 56 47 0a 20 20 20 20 20 3c 73  = <<<SVG.     <s
1030: 76 67 20 68 65 69 67 68 74 3d 32 37 30 20 77 69  vg height=270 wi
1040: 64 74 68 3d 32 31 35 20 73 74 79 6c 65 3d 27 66  dth=215 style='f
1050: 6c 6f 61 74 3a 6c 65 66 74 3b 20 6d 61 72 67 69  loat:left; margi
1060: 6e 2d 72 69 67 68 74 3a 20 33 30 70 74 3b 27 20  n-right: 30pt;' 
1070: 76 69 65 77 42 6f 78 3d 27 30 20 30 20 34 32 2e  viewBox='0 0 42.
1080: 39 36 37 38 36 31 20 35 33 2e 37 37 38 35 38 27  967861 53.77858'
1090: 3e 20 3c 67 20 74 72 61 6e 73 66 6f 72 6d 3d 27  > <g transform='
10a0: 74 72 61 6e 73 6c 61 74 65 28 2d 32 36 2e 39 32  translate(-26.92
10b0: 36 37 30 37 2c 2d 37 32 2e 32 34 34 30 34 38 29  6707,-72.244048)
10c0: 27 20 69 64 3d 27 6c 61 79 65 72 31 27 3e 0a 20  ' id='layer1'>. 
10d0: 20 20 20 20 20 20 3c 70 61 74 68 20 69 64 3d 27        <path id='
10e0: 70 61 74 68 38 32 38 27 20 64 3d 27 6d 20 35 38  path828' d='m 58
10f0: 2e 36 36 37 30 33 32 2c 37 33 2e 31 32 36 35 32  .667032,73.12652
1100: 36 20 63 20 2d 36 2e 33 35 39 34 37 2c 30 2e 30  6 c -6.35947,0.0
1110: 34 34 34 34 20 2d 31 32 2e 37 31 38 39 35 2c 30  4444 -12.71895,0
1120: 2e 30 38 38 38 38 20 2d 31 39 2e 30 37 38 34 31  .08888 -19.07841
1130: 38 2c 30 2e 31 33 33 33 32 37 20 2d 33 2e 38 35  8,0.133327 -3.85
1140: 33 36 38 33 2c 31 37 2e 32 39 33 33 35 20 2d 37  3683,17.29335 -7
1150: 2e 37 30 37 33 36 37 2c 33 34 2e 35 38 36 36 38  .707367,34.58668
1160: 37 20 2d 31 31 2e 35 36 31 30 35 2c 35 31 2e 38  7 -11.56105,51.8
1170: 38 30 30 33 37 20 34 2e 36 37 30 38 36 2c 2d 31  80037 4.67086,-1
1180: 30 65 2d 34 20 39 2e 33 34 31 37 31 39 2c 2d 30  0e-4 9.341719,-0
1190: 2e 30 30 32 20 31 34 2e 30 31 32 35 37 38 2c 2d  .002 14.012578,-
11a0: 30 2e 30 30 33 20 30 2e 31 37 38 31 31 2c 2d 36  0.003 0.17811,-6
11b0: 2e 33 32 36 32 33 20 30 2e 33 35 36 32 33 2c 2d  .32623 0.35623,-
11c0: 31 32 2e 36 35 32 34 36 20 30 2e 35 33 34 33 34  12.65246 0.53434
11d0: 2c 2d 31 38 2e 39 37 38 36 39 20 32 2e 30 34 35  ,-18.97869 2.045
11e0: 35 32 2c 2d 30 2e 38 35 38 38 36 20 34 2e 30 39  52,-0.85886 4.09
11f0: 31 30 34 2c 2d 31 2e 37 31 37 37 33 20 36 2e 31  104,-1.71773 6.1
1200: 33 36 35 37 2c 2d 32 2e 35 37 36 35 39 20 32 2e  3657,-2.57659 2.
1210: 31 33 38 38 39 2c 30 2e 38 39 35 39 20 34 2e 32  13889,0.8959 4.2
1220: 37 37 37 37 2c 31 2e 37 39 31 37 39 20 36 2e 34  7777,1.79179 6.4
1230: 31 36 36 36 2c 32 2e 36 38 37 36 39 20 2d 30 2e  1666,2.68769 -0.
1240: 31 30 35 39 34 2c 35 2e 39 36 31 36 31 20 30 2e  10594,5.96161 0.
1250: 36 34 30 35 39 2c 31 31 2e 39 33 32 34 31 20 30  64059,11.93241 0
1260: 2e 31 37 38 32 35 2c 31 37 2e 38 38 33 36 38 20  .17825,17.88368 
1270: 2d 30 2e 38 32 31 34 33 2c 31 2e 32 37 39 31 35  -0.82143,1.27915
1280: 20 31 2e 32 39 38 38 39 2c 30 2e 35 30 37 34 38   1.29889,0.50748
1290: 20 32 2e 30 35 35 33 33 2c 30 2e 37 31 38 32 37   2.05533,0.71827
12a0: 20 33 2e 38 32 30 32 33 2c 30 2e 30 30 32 20 37   3.82023,0.002 7
12b0: 2e 36 34 30 34 35 2c 30 2e 30 30 35 20 31 31 2e  .64045,0.005 11.
12c0: 34 36 30 36 37 2c 30 2e 30 30 37 20 2d 33 2e 33  46067,0.007 -3.3
12d0: 38 34 39 38 2c 2d 31 37 2e 32 35 30 37 33 20 2d  8498,-17.25073 -
12e0: 36 2e 37 36 39 39 35 2c 2d 33 34 2e 35 30 31 34  6.76995,-34.5014
12f0: 35 37 20 2d 31 30 2e 31 35 34 39 33 2c 2d 35 31  57 -10.15493,-51
1300: 2e 37 35 32 31 39 34 20 7a 20 6d 20 2d 39 2e 34  .752194 z m -9.4
1310: 38 39 33 34 2c 37 2e 35 31 38 39 32 32 20 63 20  8934,7.518922 c 
1320: 34 2e 37 36 36 33 39 2c 2d 30 2e 31 38 30 39 38  4.76639,-0.18098
1330: 38 20 38 2e 35 39 37 33 32 2c 35 2e 30 32 36 33  8 8.59732,5.0263
1340: 33 39 20 37 2e 30 32 31 36 36 2c 39 2e 35 32 31  39 7.02166,9.521
1350: 39 38 35 20 2d 31 2e 32 33 36 35 35 2c 34 2e 36  985 -1.23655,4.6
1360: 30 33 39 35 20 2d 37 2e 33 34 31 30 39 2c 36 2e  0395 -7.34109,6.
1370: 37 33 33 31 20 2d 31 31 2e 31 37 31 37 34 2c 33  7331 -11.17174,3
1380: 2e 38 39 34 31 34 20 2d 34 2e 30 33 34 34 37 34  .89414 -4.034474
1390: 2c 2d 32 2e 35 34 31 39 36 20 2d 34 2e 32 36 33  ,-2.54196 -4.263
13a0: 32 38 34 2c 2d 39 2e 30 30 32 35 37 34 20 2d 30  284,-9.002574 -0
13b0: 2e 34 31 38 37 35 2c 2d 31 31 2e 38 32 33 35 38  .41875,-11.82358
13c0: 20 31 2e 32 38 35 39 35 2c 2d 31 2e 30 32 35 38   1.28595,-1.0258
13d0: 36 38 20 32 2e 39 32 34 30 35 2c 2d 31 2e 35 39  68 2.92405,-1.59
13e0: 36 36 34 35 20 34 2e 35 36 38 38 33 2c 2d 31 2e  6645 4.56883,-1.
13f0: 35 39 32 35 34 35 20 7a 20 6d 20 35 2e 36 38 32  592545 z m 5.682
1400: 38 35 2c 34 34 2e 32 32 34 31 37 32 20 63 20 30  85,44.224172 c 0
1410: 2e 33 32 37 32 34 2c 30 20 30 2e 30 39 38 36 2c  .32724,0 0.0986,
1420: 30 20 30 2c 30 20 7a 27 0a 20 20 20 20 20 20 20  0 0,0 z'.       
1430: 73 74 79 6c 65 3d 27 66 69 6c 6c 3a 23 61 65 65  style='fill:#aee
1440: 61 34 37 3b 66 69 6c 6c 2d 6f 70 61 63 69 74 79  a47;fill-opacity
1450: 3a 31 3b 73 74 72 6f 6b 65 3a 23 34 61 35 38 34  :1;stroke:#4a584
1460: 38 3b 73 74 72 6f 6b 65 2d 77 69 64 74 68 3a 31  8;stroke-width:1
1470: 2e 37 36 34 39 39 39 39 39 3b 73 74 72 6f 6b 65  .76499999;stroke
1480: 2d 6c 69 6e 65 63 61 70 3a 62 75 74 74 3b 73 74  -linecap:butt;st
1490: 72 6f 6b 65 2d 6c 69 6e 65 6a 6f 69 6e 3a 6d 69  roke-linejoin:mi
14a0: 74 65 72 3b 73 74 72 6f 6b 65 2d 6d 69 74 65 72  ter;stroke-miter
14b0: 6c 69 6d 69 74 3a 34 3b 73 74 72 6f 6b 65 2d 64  limit:4;stroke-d
14c0: 61 73 68 61 72 72 61 79 3a 6e 6f 6e 65 3b 73 74  asharray:none;st
14d0: 72 6f 6b 65 2d 6f 70 61 63 69 74 79 3a 30 2e 39  roke-opacity:0.9
14e0: 39 34 35 36 35 31 38 27 20 2f 3e 0a 20 20 20 20  9456518' />.    
14f0: 20 3c 2f 67 3e 3c 2f 73 76 67 3e 0a 53 56 47 3b   </g></svg>.SVG;
1500: 0a 20 20 20 20 70 72 69 6e 74 28 22 3c 64 69 76  .    print("<div
1510: 20 63 6c 61 73 73 3d 27 66 6f 73 73 69 6c 2d 64   class='fossil-d
1520: 6f 63 27 20 64 61 74 61 2d 74 69 74 6c 65 3d 27  oc' data-title='
1530: 49 6e 64 69 65 41 75 74 68 27 3e 5c 6e 24 73 76  IndieAuth'>\n$sv
1540: 67 5c 6e 24 68 74 6d 6c 5c 6e 3c 2f 64 69 76 3e  g\n$html\n</div>
1550: 22 29 3b 0a 7d 0a 66 75 6e 63 74 69 6f 6e 20 6d  ");.}.function m
1560: 69 73 73 69 6e 67 5f 70 61 72 61 6d 28 24 6e 61  issing_param($na
1570: 6d 65 29 20 7b 0a 20 20 20 20 64 69 65 28 70 61  me) {.    die(pa
1580: 67 65 5f 68 74 6d 6c 28 22 3c 68 32 3e 4d 69 73  ge_html("<h2>Mis
1590: 73 69 6e 67 20 69 6e 70 75 74 3c 2f 68 32 3e 3c  sing input</h2><
15a0: 70 3e 55 52 4c 20 6c 61 63 6b 73 20 3c 63 6f 64  p>URL lacks <cod
15b0: 65 3e 26 24 6e 61 6d 65 3d 3c 2f 63 6f 64 65 3e  e>&$name=</code>
15c0: 20 70 61 72 61 6d 65 74 65 72 2e 3c 70 3e 43 61   parameter.<p>Ca
15d0: 6e 27 74 20 70 72 6f 63 65 73 73 20 61 73 20 49  n't process as I
15e0: 6e 64 69 65 41 75 74 68 2f 4f 41 75 74 68 20 72  ndieAuth/OAuth r
15f0: 65 71 75 65 73 74 2e 22 29 29 3b 0a 7d 0a 66 75  equest."));.}.fu
1600: 6e 63 74 69 6f 6e 20 70 61 67 65 5f 6d 64 28 24  nction page_md($
1610: 74 65 78 74 29 20 7b 0a 20 20 20 20 68 65 61 64  text) {.    head
1620: 65 72 28 22 43 6f 6e 74 65 6e 74 2d 54 79 70 65  er("Content-Type
1630: 3a 20 74 65 78 74 2f 78 2d 6d 61 72 6b 64 6f 77  : text/x-markdow
1640: 6e 3b 20 65 6e 63 6f 64 69 6e 67 3d 75 74 66 2d  n; encoding=utf-
1650: 38 22 29 3b 0a 20 20 20 20 70 72 69 6e 74 28 24  8");.    print($
1660: 74 65 78 74 29 3b 0a 7d 0a 66 75 6e 63 74 69 6f  text);.}.functio
1670: 6e 20 68 28 24 73 29 20 7b 0a 20 20 20 20 72 65  n h($s) {.    re
1680: 74 75 72 6e 20 68 74 6d 6c 73 70 65 63 69 61 6c  turn htmlspecial
1690: 63 68 61 72 73 28 24 73 2c 20 45 4e 54 5f 51 55  chars($s, ENT_QU
16a0: 4f 54 45 53 7c 45 4e 54 5f 48 54 4d 4c 35 2c 20  OTES|ENT_HTML5, 
16b0: 22 55 54 46 2d 38 22 29 3b 0a 7d 0a 0a 0a 23 2d  "UTF-8");.}...#-
16c0: 2d 20 74 65 73 74 20 69 66 20 68 74 74 70 3a 2f  - test if http:/
16d0: 2f 69 64 65 6e 74 69 74 79 2f 20 69 73 20 77 68  /identity/ is wh
16e0: 69 74 65 6c 69 73 74 65 64 20 69 6e 20 75 73 65  itelisted in use
16f0: 72 2e 60 68 6f 6d 65 70 61 67 65 60 2f 60 69 6e  r.`homepage`/`in
1700: 66 6f 60 20 63 6f 6c 75 6d 6e 0a 66 75 6e 63 74  fo` column.funct
1710: 69 6f 6e 20 61 6c 6c 6f 77 65 64 5f 69 64 65 6e  ion allowed_iden
1720: 74 69 74 79 28 24 75 73 65 72 2c 20 24 75 72 6c  tity($user, $url
1730: 29 20 7b 0a 20 20 20 20 24 61 6c 6c 20 3d 20 64  ) {.    $all = d
1740: 62 28 22 53 45 4c 45 43 54 20 2a 20 46 52 4f 4d  b("SELECT * FROM
1750: 20 75 73 65 72 20 57 48 45 52 45 20 6c 6f 67 69   user WHERE logi
1760: 6e 3d 3f 22 2c 20 5b 24 75 73 65 72 5d 29 3b 0a  n=?", [$user]);.
1770: 20 20 20 20 70 72 65 67 5f 6d 61 74 63 68 5f 61      preg_match_a
1780: 6c 6c 28 22 7e 5c 5c 62 20 68 74 74 70 73 3f 3a  ll("~\\b https?:
1790: 2f 2f 28 5c 53 2b 29 20 28 3f 3c 21 5b 2c 3b 7c  //(\S+) (?<![,;|
17a0: 3c 3e 27 5c 22 5d 29 7e 78 22 2c 20 6a 6f 69 6e  <>'\"])~x", join
17b0: 28 22 2c 20 22 2c 20 24 61 6c 6c 5b 30 5d 29 2c  (", ", $all[0]),
17c0: 20 24 75 75 29 3b 20 20 23 20 73 65 61 72 63 68   $uu);  # search
17d0: 20 61 6c 6c 20 66 69 65 6c 64 73 20 66 6f 72 20   all fields for 
17e0: 75 72 6c 73 0a 20 20 20 20 66 6f 72 65 61 63 68  urls.    foreach
17f0: 20 28 24 75 75 5b 31 5d 20 61 73 20 24 69 74 65   ($uu[1] as $ite
1800: 6d 29 20 7b 0a 20 20 20 20 20 20 20 20 69 66 20  m) {.        if 
1810: 28 74 72 69 6d 5f 75 72 6c 28 24 69 74 65 6d 29  (trim_url($item)
1820: 20 3d 3d 20 74 72 69 6d 5f 75 72 6c 28 24 75 72   == trim_url($ur
1830: 6c 29 29 20 7b 0a 20 20 20 20 20 20 20 20 20 20  l)) {.          
1840: 20 20 20 72 65 74 75 72 6e 20 54 72 75 65 3b 0a     return True;.
1850: 20 20 20 20 20 20 20 20 7d 0a 20 20 20 20 7d 0a          }.    }.
1860: 7d 0a 66 75 6e 63 74 69 6f 6e 20 74 72 69 6d 5f  }.function trim_
1870: 75 72 6c 28 24 75 72 6c 29 20 7b 0a 20 20 20 20  url($url) {.    
1880: 23 20 56 65 72 79 20 63 72 75 64 65 20 55 52 4c  # Very crude URL
1890: 20 65 71 75 61 6c 69 74 79 20 74 65 73 74 2e 0a   equality test..
18a0: 20 20 20 20 23 20 53 74 72 69 70 20 61 6e 79 20      # Strip any 
18b0: 68 74 74 70 3a 2f 2f 20 70 72 65 66 69 78 2c 20  http:// prefix, 
18c0: 74 72 61 69 6c 69 6e 67 20 73 6c 61 73 68 65 73  trailing slashes
18d0: 2c 20 6f 72 20 2f 68 6f 6d 65 2c 20 2f 69 6e 64  , or /home, /ind
18e0: 65 78 20 28 77 68 65 6e 20 72 65 66 65 72 72 69  ex (when referri
18f0: 6e 67 20 74 6f 20 66 6f 73 73 69 6c 20 69 6e 73  ng to fossil ins
1900: 74 61 6e 63 65 29 2e 0a 20 20 20 20 72 65 74 75  tance)..    retu
1910: 72 6e 20 73 74 72 74 6f 6c 6f 77 65 72 28 70 72  rn strtolower(pr
1920: 65 67 5f 72 65 70 6c 61 63 65 28 22 7e 20 5e 68  eg_replace("~ ^h
1930: 74 74 70 73 3f 3a 2f 2f 20 7c 20 2f 2b 28 68 6f  ttps?:// | /+(ho
1940: 6d 65 7c 69 6e 64 65 78 29 24 20 7c 20 2f 2b 24  me|index)$ | /+$
1950: 20 7e 78 22 2c 20 22 22 2c 20 24 75 72 6c 29 29   ~x", "", $url))
1960: 3b 0a 7d 0a 66 75 6e 63 74 69 6f 6e 20 62 61 73  ;.}.function bas
1970: 65 36 34 5f 75 72 6c 65 6e 63 6f 64 65 28 24 72  e64_urlencode($r
1980: 61 77 29 20 7b 0a 20 20 20 20 72 65 74 75 72 6e  aw) {.    return
1990: 20 73 74 72 74 72 28 74 72 69 6d 28 62 61 73 65   strtr(trim(base
19a0: 36 34 5f 65 6e 63 6f 64 65 28 24 72 61 77 29 2c  64_encode($raw),
19b0: 20 22 3d 22 29 2c 20 22 2b 2f 22 2c 20 22 2d 5f   "="), "+/", "-_
19c0: 22 29 3b 0a 7d 0a 0a 23 2d 2d 20 6c 6f 61 64 20  ");.}..#-- load 
19d0: 61 75 74 68 6f 72 69 7a 61 74 69 6f 6e 20 70 72  authorization pr
19e0: 6f 70 65 72 74 69 65 73 20 62 79 20 61 75 74 68  operties by auth
19f0: 20 63 6f 64 65 0a 66 75 6e 63 74 69 6f 6e 20 67   code.function g
1a00: 65 74 5f 74 6f 6b 65 6e 5f 62 79 5f 63 6f 64 65  et_token_by_code
1a10: 28 24 63 6f 64 65 29 20 7b 0a 20 20 20 20 72 65  ($code) {.    re
1a20: 74 75 72 6e 20 64 62 28 22 53 45 4c 45 43 54 20  turn db("SELECT 
1a30: 2a 20 46 52 4f 4d 20 66 78 5f 61 75 74 68 20 57  * FROM fx_auth W
1a40: 48 45 52 45 20 63 6f 64 65 3d 3f 22 2c 20 5b 24  HERE code=?", [$
1a50: 63 6f 64 65 5d 29 20 3f 3a 20 5b 5b 5d 5d 3b 0a  code]) ?: [[]];.
1a60: 7d 0a 66 75 6e 63 74 69 6f 6e 20 63 6c 65 61 6e  }.function clean
1a70: 5f 65 78 70 69 72 65 64 5f 74 6f 6b 65 6e 28 29  _expired_token()
1a80: 20 7b 0a 20 20 20 20 64 62 28 22 44 45 4c 45 54   {.    db("DELET
1a90: 45 20 46 52 4f 4d 20 66 78 5f 61 75 74 68 20 57  E FROM fx_auth W
1aa0: 48 45 52 45 20 65 78 70 69 72 65 73 20 3c 20 3f  HERE expires < ?
1ab0: 22 2c 20 5b 74 69 6d 65 28 29 5d 29 3b 0a 7d 0a  ", [time()]);.}.
1ac0: 0a 0a 23 2d 2d 20 3c 66 6f 72 6d 3e 20 63 68 65  ..#-- <form> che
1ad0: 63 6b 62 6f 78 65 73 20 66 6f 72 20 22 73 63 6f  ckboxes for "sco
1ae0: 70 65 22 20 74 6f 6b 65 6e 20 72 65 71 75 65 73  pe" token reques
1af0: 74 2c 20 65 78 74 65 6e 64 65 64 20 64 65 66 61  t, extended defa
1b00: 75 6c 74 20 6c 69 73 74 20 61 6e 64 20 3f 73 63  ult list and ?sc
1b10: 6f 70 65 3d e2 80 a6 20 72 65 71 75 65 73 74 73  ope=โ€ฆ requests
1b20: 0a 66 75 6e 63 74 69 6f 6e 20 73 63 6f 70 65 5f  .function scope_
1b30: 6c 69 73 74 28 24 68 74 6d 6c 3d 22 22 29 20 7b  list($html="") {
1b40: 0a 20 20 20 20 24 73 63 6f 70 65 73 20 3d 20 5b  .    $scopes = [
1b50: 22 63 72 65 61 74 65 22 3d 3e 22 63 68 65 63 6b  "create"=>"check
1b60: 65 64 22 2c 20 22 75 70 64 61 74 65 22 3d 3e 22  ed", "update"=>"
1b70: 63 68 65 63 6b 65 64 22 2c 20 22 64 65 6c 65 74  checked", "delet
1b80: 65 22 3d 3e 22 22 2c 20 22 74 69 63 6b 65 74 22  e"=>"", "ticket"
1b90: 3d 3e 22 63 68 65 63 6b 65 64 22 2c 20 22 77 69  =>"checked", "wi
1ba0: 6b 69 22 3d 3e 22 22 2c 20 22 66 6f 72 75 6d 22  ki"=>"", "forum"
1bb0: 3d 3e 22 22 5d 3b 0a 20 20 20 20 69 66 20 28 21  =>""];.    if (!
1bc0: 65 6d 70 74 79 28 24 5f 52 45 51 55 45 53 54 5b  empty($_REQUEST[
1bd0: 22 73 63 6f 70 65 22 5d 29 20 61 6e 64 20 70 72  "scope"]) and pr
1be0: 65 67 5f 6d 61 74 63 68 5f 61 6c 6c 28 22 2f 28  eg_match_all("/(
1bf0: 5c 77 2b 29 2f 22 2c 20 24 5f 52 45 51 55 45 53  \w+)/", $_REQUES
1c00: 54 5b 22 73 63 6f 70 65 22 5d 2c 20 24 75 75 29  T["scope"], $uu)
1c10: 29 20 7b 0a 20 20 20 20 20 20 20 20 66 6f 72 65  ) {.        fore
1c20: 61 63 68 20 28 24 75 75 5b 31 5d 20 61 73 20 24  ach ($uu[1] as $
1c30: 66 69 65 6c 64 29 20 7b 20 24 73 63 6f 70 65 73  field) { $scopes
1c40: 5b 24 66 69 65 6c 64 5d 20 3d 20 22 63 68 65 63  [$field] = "chec
1c50: 6b 65 64 22 3b 20 7d 0a 20 20 20 20 7d 0a 20 20  ked"; }.    }.  
1c60: 20 20 66 6f 72 65 61 63 68 20 28 24 73 63 6f 70    foreach ($scop
1c70: 65 73 20 61 73 20 24 66 69 65 6c 64 3d 3e 24 63  es as $field=>$c
1c80: 68 65 63 6b 65 64 29 20 7b 0a 20 20 20 20 20 20  hecked) {.      
1c90: 20 20 24 68 74 6d 6c 20 2e 3d 20 22 3c 6c 69 20    $html .= "<li 
1ca0: 73 74 79 6c 65 3d 27 66 6c 65 78 3a 20 31 20 30  style='flex: 1 0
1cb0: 20 33 33 25 3b 20 6c 69 73 74 2d 73 74 79 6c 65   33%; list-style
1cc0: 2d 74 79 70 65 3a 20 6e 6f 6e 65 3b 27 3e 20 3c  -type: none;'> <
1cd0: 6c 61 62 65 6c 3e 3c 69 6e 70 75 74 20 74 79 70  label><input typ
1ce0: 65 3d 63 68 65 63 6b 62 6f 78 20 24 63 68 65 63  e=checkbox $chec
1cf0: 6b 65 64 20 6e 61 6d 65 3d 27 73 63 6f 70 65 5b  ked name='scope[
1d00: 5d 27 20 76 61 6c 75 65 3d 24 66 69 65 6c 64 3e  ]' value=$field>
1d10: 20 24 66 69 65 6c 64 3c 2f 6c 61 62 65 6c 3e 5c   $field</label>\
1d20: 6e 22 3b 0a 20 20 20 20 7d 0a 20 20 20 20 72 65  n";.    }.    re
1d30: 74 75 72 6e 20 24 68 74 6d 6c 3b 0a 7d 0a 0a 23  turn $html;.}..#
1d40: 2d 2d 20 69 6e 69 74 69 61 6c 20 61 75 74 68 6f  -- initial autho
1d50: 72 69 7a 61 74 69 6f 6e 20 72 65 71 75 65 73 74  rization request
1d60: 0a 66 75 6e 63 74 69 6f 6e 20 70 72 6f 63 65 73  .function proces
1d70: 73 5f 72 65 71 75 65 73 74 28 29 20 7b 0a 0a 20  s_request() {.. 
1d80: 20 20 20 23 20 69 6e 70 75 74 20 70 61 72 61 6d     # input param
1d90: 73 0a 20 20 20 20 24 75 73 65 72 20 3d 20 24 5f  s.    $user = $_
1da0: 53 45 52 56 45 52 5b 22 46 4f 53 53 49 4c 5f 55  SERVER["FOSSIL_U
1db0: 53 45 52 22 5d 3b 0a 20 20 20 20 24 73 65 63 72  SER"];.    $secr
1dc0: 65 74 20 3d 20 24 5f 53 45 52 56 45 52 5b 22 46  et = $_SERVER["F
1dd0: 4f 53 53 49 4c 5f 4e 4f 4e 43 45 22 5d 3b 0a 20  OSSIL_NONCE"];. 
1de0: 20 20 20 24 6d 65 20 3d 20 24 5f 52 45 51 55 45     $me = $_REQUE
1df0: 53 54 5b 22 6d 65 22 5d 20 6f 72 20 6d 69 73 73  ST["me"] or miss
1e00: 69 6e 67 5f 70 61 72 61 6d 28 22 6d 65 22 29 3b  ing_param("me");
1e10: 0a 20 20 20 20 24 63 6c 69 65 6e 74 5f 69 64 20  .    $client_id 
1e20: 3d 20 24 5f 52 45 51 55 45 53 54 5b 22 63 6c 69  = $_REQUEST["cli
1e30: 65 6e 74 5f 69 64 22 5d 20 6f 72 20 6d 69 73 73  ent_id"] or miss
1e40: 69 6e 67 5f 70 61 72 61 6d 28 22 63 6c 69 65 6e  ing_param("clien
1e50: 74 5f 69 64 22 29 3b 0a 20 20 20 20 24 72 65 64  t_id");.    $red
1e60: 69 72 65 63 74 5f 75 72 69 20 3d 20 24 5f 52 45  irect_uri = $_RE
1e70: 51 55 45 53 54 5b 22 72 65 64 69 72 65 63 74 5f  QUEST["redirect_
1e80: 75 72 69 22 5d 20 6f 72 20 6d 69 73 73 69 6e 67  uri"] or missing
1e90: 5f 70 61 72 61 6d 28 22 72 65 64 69 72 65 63 74  _param("redirect
1ea0: 5f 75 72 69 22 29 3b 0a 20 20 20 20 24 73 74 61  _uri");.    $sta
1eb0: 74 65 20 3d 20 24 5f 52 45 51 55 45 53 54 5b 22  te = $_REQUEST["
1ec0: 73 74 61 74 65 22 5d 20 3f 3a 20 22 22 3b 0a 20  state"] ?: "";. 
1ed0: 20 20 20 24 63 6f 64 65 5f 63 68 61 6c 6c 65 6e     $code_challen
1ee0: 67 65 20 3d 20 24 5f 52 45 51 55 45 53 54 5b 22  ge = $_REQUEST["
1ef0: 63 6f 64 65 5f 63 68 61 6c 6c 65 6e 67 65 22 5d  code_challenge"]
1f00: 20 3f 3a 20 22 22 3b 0a 20 20 20 20 24 63 6f 64   ?: "";.    $cod
1f10: 65 5f 63 68 61 6c 6c 65 6e 67 65 5f 6d 20 3d 20  e_challenge_m = 
1f20: 24 5f 52 45 51 55 45 53 54 5b 22 63 6f 64 65 5f  $_REQUEST["code_
1f30: 63 68 61 6c 6c 65 6e 67 65 5f 6d 65 74 68 6f 64  challenge_method
1f40: 22 5d 20 3f 3a 20 22 53 32 35 36 22 3b 0a 20 20  "] ?: "S256";.  
1f50: 20 20 24 72 65 73 70 6f 6e 73 65 5f 74 79 70 65    $response_type
1f60: 20 3d 20 24 5f 52 45 51 55 45 53 54 5b 22 72 65   = $_REQUEST["re
1f70: 73 70 6f 6e 73 65 5f 74 79 70 65 22 5d 20 3f 3a  sponse_type"] ?:
1f80: 20 22 69 64 22 3b 0a 20 20 20 20 24 68 20 3d 20   "id";.    $h = 
1f90: 22 68 22 3b 0a 0a 20 20 20 20 23 20 63 68 65 63  "h";..    # chec
1fa0: 6b 20 69 66 20 24 6d 65 20 69 73 20 61 6c 6c 6f  k if $me is allo
1fb0: 77 65 64 0a 20 20 20 20 69 66 20 28 21 61 6c 6c  wed.    if (!all
1fc0: 6f 77 65 64 5f 69 64 65 6e 74 69 74 79 28 24 75  owed_identity($u
1fd0: 73 65 72 2c 20 24 6d 65 29 29 20 7b 0a 20 20 20  ser, $me)) {.   
1fe0: 20 20 20 20 20 72 65 74 75 72 6e 20 70 61 67 65       return page
1ff0: 5f 68 74 6d 6c 28 22 3c 68 32 3e 49 6e 76 61 6c  _html("<h2>Inval
2000: 69 64 20 69 64 65 6e 74 69 74 79 3c 2f 68 32 3e  id identity</h2>
2010: 20 55 73 65 72 20 64 6f 65 73 6e 27 74 20 68 61   User doesn't ha
2020: 76 65 20 27 7b 24 68 28 24 6d 65 29 7d 27 20 72  ve '{$h($me)}' r
2030: 65 73 65 72 76 65 64 20 69 6e 20 75 73 65 72 20  eserved in user 
2040: 74 61 62 6c 65 2e 22 29 3b 0a 20 20 20 20 7d 0a  table.");.    }.
2050: 20 20 20 20 0a 20 20 20 20 23 20 6e 65 77 20 63      .    # new c
2060: 6f 64 65 20 2f 2f 20 68 61 73 68 69 6e 67 20 74  ode // hashing t
2070: 68 65 20 70 72 6f 70 65 72 74 69 65 73 20 69 73  he properties is
2080: 20 61 20 62 69 74 20 6f 76 65 72 6b 69 6c 6c 2c   a bit overkill,
2090: 20 61 6e 79 20 72 61 6e 64 6f 6d 20 69 64 20 77   any random id w
20a0: 6f 75 6c 64 20 73 75 66 66 69 63 65 0a 20 20 20  ould suffice.   
20b0: 20 24 63 6f 64 65 20 3d 20 70 61 73 73 77 6f 72   $code = passwor
20c0: 64 5f 68 61 73 68 28 22 24 6d 65 2f 24 63 6c 69  d_hash("$me/$cli
20d0: 65 6e 74 5f 69 64 2f 24 73 74 61 74 65 2f 24 73  ent_id/$state/$s
20e0: 65 63 72 65 74 22 2c 20 50 41 53 53 57 4f 52 44  ecret", PASSWORD
20f0: 5f 44 45 46 41 55 4c 54 29 3b 0a 20 20 20 20 64  _DEFAULT);.    d
2100: 62 28 22 0a 20 20 20 20 20 20 20 20 49 4e 53 45  b(".        INSE
2110: 52 54 20 49 4e 54 4f 20 66 78 5f 61 75 74 68 0a  RT INTO fx_auth.
2120: 20 20 20 20 20 20 20 20 28 60 63 6f 64 65 60 2c          (`code`,
2130: 20 60 74 79 70 65 60 2c 20 60 6c 6f 67 69 6e 60   `type`, `login`
2140: 2c 20 60 6d 65 60 2c 20 60 63 6c 69 65 6e 74 5f  , `me`, `client_
2150: 69 64 60 2c 20 60 72 65 64 69 72 65 63 74 5f 75  id`, `redirect_u
2160: 72 69 60 2c 20 60 73 74 61 74 65 60 2c 20 60 63  ri`, `state`, `c
2170: 6f 64 65 5f 63 68 61 6c 6c 65 6e 67 65 60 2c 20  ode_challenge`, 
2180: 60 63 6f 64 65 5f 63 68 61 6c 6c 65 6e 67 65 5f  `code_challenge_
2190: 6d 60 2c 20 60 65 78 70 69 72 65 73 60 29 0a 20  m`, `expires`). 
21a0: 20 20 20 20 20 20 20 56 41 4c 55 45 53 20 28 3f         VALUES (?
21b0: 2c 20 3f 2c 20 3f 2c 20 3f 2c 20 3f 2c 20 3f 2c  , ?, ?, ?, ?, ?,
21c0: 20 3f 2c 20 3f 2c 20 3f 2c 20 3f 29 22 2c 0a 20   ?, ?, ?, ?)",. 
21d0: 20 20 20 20 20 20 20 5b 24 63 6f 64 65 2c 20 24         [$code, $
21e0: 72 65 73 70 6f 6e 73 65 5f 74 79 70 65 2c 20 24  response_type, $
21f0: 75 73 65 72 2c 20 24 6d 65 2c 20 24 63 6c 69 65  user, $me, $clie
2200: 6e 74 5f 69 64 2c 20 24 72 65 64 69 72 65 63 74  nt_id, $redirect
2210: 5f 75 72 69 2c 20 24 73 74 61 74 65 2c 20 24 63  _uri, $state, $c
2220: 6f 64 65 5f 63 68 61 6c 6c 65 6e 67 65 2c 20 24  ode_challenge, $
2230: 63 6f 64 65 5f 63 68 61 6c 6c 65 6e 67 65 5f 6d  code_challenge_m
2240: 2c 20 74 69 6d 65 28 29 2b 33 30 30 5d 0a 20 20  , time()+300].  
2250: 20 20 29 3b 0a 0a 20 20 20 20 23 20 63 6f 6e 73    );..    # cons
2260: 74 72 75 63 74 20 63 6f 6e 66 69 72 6d 61 74 69  truct confirmati
2270: 6f 6e 2b 72 65 64 69 72 65 63 74 20 75 72 6c 0a  on+redirect url.
2280: 20 20 20 20 24 75 72 6c 20 3d 20 24 72 65 64 69      $url = $redi
2290: 72 65 63 74 5f 75 72 69 0a 20 20 20 20 20 20 20  rect_uri.       
22a0: 20 20 2e 20 28 73 74 72 73 74 72 28 24 72 65 64    . (strstr($red
22b0: 69 72 65 63 74 5f 75 72 69 2c 20 22 3f 22 29 20  irect_uri, "?") 
22c0: 3f 20 22 26 22 20 3a 20 22 3f 22 29 0a 20 20 20  ? "&" : "?").   
22d0: 20 20 20 20 20 20 2e 20 22 63 6f 64 65 3d 22 20        . "code=" 
22e0: 2e 20 75 72 6c 65 6e 63 6f 64 65 28 24 63 6f 64  . urlencode($cod
22f0: 65 29 20 2e 20 22 26 73 74 61 74 65 3d 22 20 2e  e) . "&state=" .
2300: 20 75 72 6c 65 6e 63 6f 64 65 28 24 73 74 61 74   urlencode($stat
2310: 65 29 3b 0a 20 20 20 20 0a 20 20 20 20 23 20 6f  e);.    .    # o
2320: 75 74 70 75 74 20 70 61 67 65 0a 20 20 20 20 69  utput page.    i
2330: 66 20 28 24 72 65 73 70 6f 6e 73 65 5f 74 79 70  f ($response_typ
2340: 65 20 3d 3d 20 22 63 6f 64 65 22 29 20 7b 0a 20  e == "code") {. 
2350: 20 20 20 20 20 20 20 24 73 63 6f 70 65 20 3d 20         $scope = 
2360: 73 63 6f 70 65 5f 6c 69 73 74 28 29 3b 0a 20 20  scope_list();.  
2370: 20 20 7d 0a 20 20 20 20 24 68 74 6d 6c 20 3d 20    }.    $html = 
2380: 3c 3c 3c 48 54 4d 4c 0a 0a 20 20 20 20 3c 68 32  <<<HTML..    <h2
2390: 3e 4c 6f 67 69 6e 20 72 65 71 75 65 73 74 3c 2f  >Login request</
23a0: 68 32 3e 0a 0a 20 20 20 20 3c 21 2d 2d 20 49 6e  h2>..    <!-- In
23b0: 64 69 65 41 75 74 68 20 2d 2d 3e 0a 20 20 20 20  dieAuth -->.    
23c0: 3c 70 3e 3c 62 3e 7b 24 68 28 24 63 6c 69 65 6e  <p><b>{$h($clien
23d0: 74 5f 69 64 29 7d 3c 2f 62 3e 20 68 61 73 20 72  t_id)}</b> has r
23e0: 65 71 75 65 73 74 65 64 20 6c 6f 67 69 6e 20 76  equested login v
23f0: 65 72 69 66 69 63 61 74 69 6f 6e 20 66 6f 72 20  erification for 
2400: 3c 65 6d 3e 7b 24 68 28 24 6d 65 29 7d 3c 2f 65  <em>{$h($me)}</e
2410: 6d 3e 2e 3c 2f 70 3e 0a 20 20 20 20 3c 66 6f 72  m>.</p>.    <for
2420: 6d 20 61 63 74 69 6f 6e 3d 27 24 5f 53 45 52 56  m action='$_SERV
2430: 45 52 5b 50 48 50 5f 53 45 4c 46 5d 27 20 6d 65  ER[PHP_SELF]' me
2440: 74 68 6f 64 3d 50 4f 53 54 3e 0a 20 20 20 20 20  thod=POST>.     
2450: 20 3c 75 6c 20 73 74 79 6c 65 3d 22 64 69 73 70   <ul style="disp
2460: 6c 61 79 3a 20 66 6c 65 78 3b 20 66 6c 65 78 2d  lay: flex; flex-
2470: 77 72 61 70 3a 20 77 72 61 70 22 3e 0a 20 20 20  wrap: wrap">.   
2480: 20 20 20 20 20 20 24 73 63 6f 70 65 0a 20 20 20        $scope.   
2490: 20 20 20 3c 2f 75 6c 3e 0a 20 20 20 20 20 20 3c     </ul>.      <
24a0: 70 3e 0a 20 20 20 20 20 20 20 20 3c 69 6e 70 75  p>.        <inpu
24b0: 74 20 74 79 70 65 3d 68 69 64 64 65 6e 20 6e 61  t type=hidden na
24c0: 6d 65 3d 63 6f 64 65 20 76 61 6c 75 65 3d 27 7b  me=code value='{
24d0: 24 68 28 24 63 6f 64 65 29 7d 27 3e 0a 20 20 20  $h($code)}'>.   
24e0: 20 20 20 20 20 3c 69 6e 70 75 74 20 74 79 70 65       <input type
24f0: 3d 68 69 64 64 65 6e 20 6e 61 6d 65 3d 72 65 64  =hidden name=red
2500: 69 72 65 63 74 5f 74 61 72 67 65 74 20 76 61 6c  irect_target val
2510: 75 65 3d 27 7b 24 68 28 24 75 72 6c 29 7d 27 3e  ue='{$h($url)}'>
2520: 0a 20 20 20 20 20 20 20 20 3c 69 6e 70 75 74 20  .        <input 
2530: 74 79 70 65 3d 73 75 62 6d 69 74 20 6e 61 6d 65  type=submit name
2540: 3d 63 6f 6e 66 69 72 6d 20 76 61 6c 75 65 3d 43  =confirm value=C
2550: 6f 6e 66 69 72 6d 20 73 74 79 6c 65 3d 27 62 6f  onfirm style='bo
2560: 72 64 65 72 2d 72 61 64 69 75 73 3a 20 35 70 74  rder-radius: 5pt
2570: 3b 20 70 61 64 64 69 6e 67 3a 20 35 70 74 20 32  ; padding: 5pt 2
2580: 35 70 74 3b 20 74 65 78 74 2d 73 68 61 64 6f 77  5pt; text-shadow
2590: 3a 20 31 70 74 3b 27 3e 0a 20 20 20 20 20 20 3c  : 1pt;'>.      <
25a0: 2f 70 3e 0a 20 20 20 20 3c 2f 66 6f 72 6d 3e 0a  /p>.    </form>.
25b0: 0a 48 54 4d 4c 3b 0a 20 20 20 20 70 61 67 65 5f  .HTML;.    page_
25c0: 68 74 6d 6c 28 24 68 74 6d 6c 29 3b 0a 7d 0a 0a  html($html);.}..
25d0: 23 2d 2d 20 63 6f 6e 66 69 72 6d 20 62 75 74 74  #-- confirm butt
25e0: 6f 6e 20 70 72 65 73 73 65 64 2c 20 64 6f 20 74  on pressed, do t
25f0: 68 65 20 61 63 74 75 61 6c 20 72 65 64 69 72 65  he actual redire
2600: 63 74 0a 66 75 6e 63 74 69 6f 6e 20 63 6f 6e 66  ct.function conf
2610: 69 72 6d 28 29 20 7b 0a 20 20 20 20 69 66 20 28  irm() {.    if (
2620: 21 65 6d 70 74 79 28 24 5f 50 4f 53 54 5b 22 73  !empty($_POST["s
2630: 63 6f 70 65 22 5d 29 29 20 7b 0a 20 20 20 20 20  cope"])) {.     
2640: 20 20 20 64 62 28 22 55 50 44 41 54 45 20 66 78     db("UPDATE fx
2650: 5f 61 75 74 68 20 53 45 54 20 73 63 6f 70 65 3d  _auth SET scope=
2660: 3f 20 57 48 45 52 45 20 63 6f 64 65 3d 3f 22 2c  ? WHERE code=?",
2670: 20 5b 20 20 69 6d 70 6c 6f 64 65 28 22 20 22 2c   [  implode(" ",
2680: 20 24 5f 50 4f 53 54 5b 22 73 63 6f 70 65 22 5d   $_POST["scope"]
2690: 29 2c 20 20 24 5f 50 4f 53 54 5b 22 63 6f 64 65  ),  $_POST["code
26a0: 22 5d 20 20 5d 29 3b 0a 20 20 20 20 7d 0a 20 20  "]  ]);.    }.  
26b0: 20 20 64 69 65 28 68 65 61 64 65 72 28 22 4c 6f    die(header("Lo
26c0: 63 61 74 69 6f 6e 3a 20 24 5f 50 4f 53 54 5b 72  cation: $_POST[r
26d0: 65 64 69 72 65 63 74 5f 74 61 72 67 65 74 5d 22  edirect_target]"
26e0: 29 29 3b 0a 7d 0a 0a 0a 23 2d 2d 20 73 65 6e 64  ));.}...#-- send
26f0: 20 3f 63 6f 64 65 3d 20 76 65 72 69 66 69 63 61   ?code= verifica
2700: 74 69 6f 6e 20 72 65 73 70 6f 6e 73 65 0a 66 75  tion response.fu
2710: 6e 63 74 69 6f 6e 20 76 65 72 69 66 79 5f 63 6f  nction verify_co
2720: 64 65 28 29 20 7b 0a 20 20 20 20 68 65 61 64 65  de() {.    heade
2730: 72 28 22 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a  r("Content-Type:
2740: 20 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6a 73 6f   application/jso
2750: 6e 22 29 3b 0a 0a 20 20 20 20 23 20 69 6e 70 75  n");..    # inpu
2760: 74 0a 20 20 20 20 24 67 72 61 6e 74 5f 74 79 70  t.    $grant_typ
2770: 65 20 3d 20 24 5f 52 45 51 55 45 53 54 5b 22 67  e = $_REQUEST["g
2780: 72 61 6e 74 5f 74 79 70 65 22 5d 3b 0a 20 20 20  rant_type"];.   
2790: 20 24 63 6f 64 65 20 3d 20 24 5f 52 45 51 55 45   $code = $_REQUE
27a0: 53 54 5b 22 63 6f 64 65 22 5d 3b 0a 20 20 20 20  ST["code"];.    
27b0: 24 63 6c 69 65 6e 74 5f 69 64 20 3d 20 24 5f 52  $client_id = $_R
27c0: 45 51 55 45 53 54 5b 22 63 6c 69 65 6e 74 5f 69  EQUEST["client_i
27d0: 64 22 5d 3b 0a 20 20 20 20 24 72 65 64 69 72 65  d"];.    $redire
27e0: 63 74 5f 75 72 69 20 3d 20 24 5f 52 45 51 55 45  ct_uri = $_REQUE
27f0: 53 54 5b 22 72 65 64 69 72 65 63 74 5f 75 72 69  ST["redirect_uri
2800: 22 5d 3b 0a 20 20 20 20 24 63 6f 64 65 5f 76 65  "];.    $code_ve
2810: 72 69 66 69 65 72 20 3d 20 24 5f 52 45 51 55 45  rifier = $_REQUE
2820: 53 54 5b 22 63 6f 64 65 5f 76 65 72 69 66 69 65  ST["code_verifie
2830: 72 22 5d 3b 0a 20 20 20 20 0a 20 20 20 20 23 20  r"];.    .    # 
2840: 66 69 6e 64 20 74 6f 6b 65 6e 0a 20 20 20 20 63  find token.    c
2850: 6c 65 61 6e 5f 65 78 70 69 72 65 64 5f 74 6f 6b  lean_expired_tok
2860: 65 6e 28 29 3b 0a 20 20 20 20 24 74 6f 6b 65 6e  en();.    $token
2870: 20 3d 20 67 65 74 5f 74 6f 6b 65 6e 5f 62 79 5f   = get_token_by_
2880: 63 6f 64 65 28 24 63 6f 64 65 29 5b 30 5d 3b 0a  code($code)[0];.
2890: 20 20 20 20 24 63 6f 64 65 5f 63 68 61 6c 6c 65      $code_challe
28a0: 6e 67 65 20 3d 20 24 74 6f 6b 65 6e 5b 22 63 6f  nge = $token["co
28b0: 64 65 5f 63 68 61 6c 6c 65 6e 67 65 22 5d 3b 0a  de_challenge"];.
28c0: 20 20 20 20 0a 20 20 20 20 23 20 63 68 65 63 6b      .    # check
28d0: 20 70 61 72 61 6d 73 0a 20 20 20 20 69 66 20 28   params.    if (
28e0: 65 6d 70 74 79 28 24 63 6f 64 65 29 20 6f 72 20  empty($code) or 
28f0: 65 6d 70 74 79 28 24 63 6c 69 65 6e 74 5f 69 64  empty($client_id
2900: 29 20 6f 72 20 65 6d 70 74 79 28 24 72 65 64 69  ) or empty($redi
2910: 72 65 63 74 5f 75 72 69 29 29 20 7b 0a 20 20 20  rect_uri)) {.   
2920: 20 20 20 20 20 24 6c 73 20 3d 20 6a 6f 69 6e 28       $ls = join(
2930: 22 2c 20 22 2c 20 61 72 72 61 79 5f 64 69 66 66  ", ", array_diff
2940: 28 5b 22 63 6f 64 65 22 2c 20 22 63 6c 69 65 6e  (["code", "clien
2950: 74 5f 69 64 22 2c 20 22 72 65 64 69 72 65 63 74  t_id", "redirect
2960: 5f 75 72 69 22 5d 2c 20 61 72 72 61 79 5f 6b 65  _uri"], array_ke
2970: 79 73 28 24 5f 52 45 51 55 45 53 54 29 29 29 3b  ys($_REQUEST)));
2980: 0a 20 20 20 20 20 20 20 20 64 69 65 28 6a 73 6f  .        die(jso
2990: 6e 5f 65 6e 63 6f 64 65 28 5b 22 65 72 72 6f 72  n_encode(["error
29a0: 22 20 3d 3e 20 20 22 69 6e 76 61 6c 69 64 5f 72  " =>  "invalid_r
29b0: 65 71 75 65 73 74 22 2c 20 22 65 72 72 6f 72 5f  equest", "error_
29c0: 64 65 73 63 72 69 70 74 69 6f 6e 22 20 3d 3e 20  description" => 
29d0: 22 6d 69 73 73 69 6e 67 20 70 61 72 61 6d 65 74  "missing paramet
29e0: 65 72 73 20 28 24 6c 73 29 22 5d 29 29 3b 0a 20  ers ($ls)"]));. 
29f0: 20 20 20 7d 0a 20 20 20 20 65 6c 73 65 69 66 20     }.    elseif 
2a00: 28 65 6d 70 74 79 28 24 74 6f 6b 65 6e 29 29 20  (empty($token)) 
2a10: 7b 0a 20 20 20 20 20 20 20 20 64 69 65 28 6a 73  {.        die(js
2a20: 6f 6e 5f 65 6e 63 6f 64 65 28 5b 22 65 72 72 6f  on_encode(["erro
2a30: 72 22 20 3d 3e 20 20 22 61 63 63 65 73 73 5f 64  r" =>  "access_d
2a40: 65 6e 69 65 64 22 2c 20 22 65 72 72 6f 72 5f 64  enied", "error_d
2a50: 65 73 63 72 69 70 74 69 6f 6e 22 20 3d 3e 20 22  escription" => "
2a60: 63 6f 64 65 20 27 24 63 6f 64 65 27 20 64 6f 65  code '$code' doe
2a70: 73 20 6e 6f 74 20 65 78 69 73 74 20 28 70 6f 73  s not exist (pos
2a80: 73 69 62 6c 79 20 65 78 70 69 72 65 64 29 22 5d  sibly expired)"]
2a90: 29 29 3b 0a 20 20 20 20 7d 0a 20 20 20 20 65 6c  ));.    }.    el
2aa0: 73 65 69 66 20 28 28 24 74 6f 6b 65 6e 5b 22 63  seif (($token["c
2ab0: 6c 69 65 6e 74 5f 69 64 22 5d 20 21 3d 20 24 63  lient_id"] != $c
2ac0: 6c 69 65 6e 74 5f 69 64 29 20 6f 72 20 28 24 74  lient_id) or ($t
2ad0: 6f 6b 65 6e 5b 22 72 65 64 69 72 65 63 74 5f 75  oken["redirect_u
2ae0: 72 69 22 5d 20 21 3d 20 24 72 65 64 69 72 65 63  ri"] != $redirec
2af0: 74 5f 75 72 69 29 29 20 7b 0a 20 20 20 20 20 20  t_uri)) {.      
2b00: 20 20 64 69 65 28 6a 73 6f 6e 5f 65 6e 63 6f 64    die(json_encod
2b10: 65 28 5b 22 65 72 72 6f 72 22 20 3d 3e 20 20 22  e(["error" =>  "
2b20: 69 6e 76 61 6c 69 64 5f 73 63 6f 70 65 22 2c 20  invalid_scope", 
2b30: 22 65 72 72 6f 72 5f 64 65 73 63 72 69 70 74 69  "error_descripti
2b40: 6f 6e 22 20 3d 3e 20 22 63 6f 64 65 20 64 6f 65  on" => "code doe
2b50: 73 20 6e 6f 74 20 6d 61 74 63 68 20 70 72 65 76  s not match prev
2b60: 69 6f 75 73 20 70 61 72 61 6d 73 20 28 63 6c 69  ious params (cli
2b70: 65 6e 74 5f 69 64 2c 20 72 65 64 69 72 65 63 74  ent_id, redirect
2b80: 5f 75 72 69 29 22 5d 29 29 3b 0a 20 20 20 20 7d  _uri)"]));.    }
2b90: 0a 20 20 20 20 65 6c 73 65 69 66 20 28 24 63 6f  .    elseif ($co
2ba0: 64 65 5f 63 68 61 6c 6c 65 6e 67 65 20 61 6e 64  de_challenge and
2bb0: 20 62 61 73 65 36 34 5f 75 72 6c 65 6e 63 6f 64   base64_urlencod
2bc0: 65 28 68 61 73 68 28 22 73 68 61 32 35 36 22 2c  e(hash("sha256",
2bd0: 20 24 63 6f 64 65 5f 76 65 72 69 66 69 65 72 2c   $code_verifier,
2be0: 20 31 29 29 20 21 3d 20 24 63 6f 64 65 5f 63 68   1)) != $code_ch
2bf0: 61 6c 6c 65 6e 67 65 29 20 7b 0a 20 20 20 20 20  allenge) {.     
2c00: 20 20 20 64 69 65 28 6a 73 6f 6e 5f 65 6e 63 6f     die(json_enco
2c10: 64 65 28 5b 22 65 72 72 6f 72 22 20 3d 3e 20 20  de(["error" =>  
2c20: 22 75 6e 61 75 74 68 6f 72 69 7a 65 64 5f 63 6c  "unauthorized_cl
2c30: 69 65 6e 74 22 2c 20 22 65 72 72 6f 72 5f 64 65  ient", "error_de
2c40: 73 63 72 69 70 74 69 6f 6e 22 20 3d 3e 20 22 63  scription" => "c
2c50: 6f 64 65 5f 63 68 61 6c 6c 65 6e 67 65 20 64 6f  ode_challenge do
2c60: 65 73 20 6e 6f 74 20 6d 61 74 63 68 20 63 6f 64  es not match cod
2c70: 65 5f 76 65 72 69 66 69 65 72 22 5d 29 29 3b 0a  e_verifier"]));.
2c80: 20 20 20 20 7d 0a 20 20 20 20 65 6c 73 65 20 7b      }.    else {
2c90: 0a 20 20 20 20 20 20 20 20 23 20 61 70 70 72 6f  .        # appro
2ca0: 76 65 64 0a 20 20 20 20 20 20 20 20 64 69 65 28  ved.        die(
2cb0: 6a 73 6f 6e 5f 65 6e 63 6f 64 65 28 5b 22 6d 65  json_encode(["me
2cc0: 22 20 3d 3e 20 24 74 6f 6b 65 6e 5b 22 6d 65 22  " => $token["me"
2cd0: 5d 2c 20 22 73 63 6f 70 65 22 20 3d 3e 20 24 74  ], "scope" => $t
2ce0: 6f 6b 65 6e 5b 22 73 63 6f 70 65 22 5d 5d 29 29  oken["scope"]]))
2cf0: 3b 0a 20 20 20 20 7d 0a 7d 0a 0a 0a 23 2d 2d 20  ;.    }.}...#-- 
2d00: 72 75 6e 0a 69 66 20 28 65 6d 70 74 79 28 24 5f  run.if (empty($_
2d10: 50 4f 53 54 5b 22 72 65 64 69 72 65 63 74 5f 74  POST["redirect_t
2d20: 61 72 67 65 74 22 5d 29 20 61 6e 64 20 21 65 6d  arget"]) and !em
2d30: 70 74 79 28 24 5f 52 45 51 55 45 53 54 5b 22 63  pty($_REQUEST["c
2d40: 6f 64 65 22 5d 29 29 20 7b 20 20 23 20 3f 63 6f  ode"])) {  # ?co
2d50: 64 65 3d e2 80 a6 20 77 68 65 6e 20 74 68 65 20  de=โ€ฆ when the 
2d60: 72 65 6d 6f 74 65 20 61 70 70 20 76 65 72 69 66  remote app verif
2d70: 69 65 73 20 74 68 65 20 72 65 73 70 6f 6e 73 65  ies the response
2d80: 0a 20 20 20 20 76 65 72 69 66 79 5f 63 6f 64 65  .    verify_code
2d90: 28 29 3b 0a 7d 0a 65 6c 73 65 69 66 20 28 65 6d  ();.}.elseif (em
2da0: 70 74 79 28 24 5f 53 45 52 56 45 52 5b 22 46 4f  pty($_SERVER["FO
2db0: 53 53 49 4c 5f 55 53 45 52 22 5d 29 29 20 7b 20  SSIL_USER"])) { 
2dc0: 20 20 23 20 75 73 65 72 20 6d 75 73 74 20 62 65    # user must be
2dd0: 20 73 69 67 6e 65 64 20 69 6e 20 61 74 20 74 68   signed in at th
2de0: 69 73 20 70 6f 69 6e 74 0a 20 20 20 20 70 61 67  is point.    pag
2df0: 65 5f 68 74 6d 6c 28 22 3c 68 32 3e 4e 6f 74 20  e_html("<h2>Not 
2e00: 6c 6f 67 67 65 64 20 69 6e 3c 2f 68 32 3e 5c 6e  logged in</h2>\n
2e10: 20 52 65 71 75 65 73 74 20 63 61 6e 27 74 20 62   Request can't b
2e20: 65 20 61 75 74 68 6f 72 69 7a 65 64 2c 20 75 6e  e authorized, un
2e30: 6c 65 73 73 20 79 6f 75 27 72 65 20 3c 61 20 68  less you're <a h
2e40: 72 65 66 3d 27 2e 2e 2f 6c 6f 67 69 6e 27 3e 6c  ref='../login'>l
2e50: 6f 67 67 65 64 20 69 6e 3c 2f 61 3e 2e 22 29 3b  ogged in</a>.");
2e60: 0a 7d 0a 65 6c 73 65 69 66 20 28 21 65 6d 70 74  .}.elseif (!empt
2e70: 79 28 24 5f 52 45 51 55 45 53 54 5b 22 6d 65 22  y($_REQUEST["me"
2e80: 5d 29 29 20 7b 20 20 20 23 20 3f 6d 65 3d e2 80  ])) {   # ?me=โ€
2e90: a6 20 73 74 61 72 74 73 20 61 6e 20 61 75 74 68  ฆ starts an auth
2ea0: 6f 72 69 7a 61 74 69 6f 6e 20 72 65 71 75 65 73  orization reques
2eb0: 74 0a 20 20 20 20 70 72 6f 63 65 73 73 5f 72 65  t.    process_re
2ec0: 71 75 65 73 74 28 29 3b 0a 7d 0a 65 6c 73 65 69  quest();.}.elsei
2ed0: 66 20 28 21 65 6d 70 74 79 28 24 5f 50 4f 53 54  f (!empty($_POST
2ee0: 5b 22 63 6f 6e 66 69 72 6d 22 5d 29 29 20 7b 20  ["confirm"])) { 
2ef0: 20 20 23 20 3f 72 65 64 69 72 65 63 74 5f 74 61    # ?redirect_ta
2f00: 72 67 65 74 3d e2 80 a6 20 20 66 6f 72 20 63 6f  rget=โ€ฆ  for co
2f10: 6e 66 69 72 6d 61 74 69 6f 6e 20 62 75 74 74 6f  nfirmation butto
2f20: 6e 0a 20 20 20 20 63 6f 6e 66 69 72 6d 28 29 3b  n.    confirm();
2f30: 0a 7d 0a 65 6c 73 65 20 7b 0a 20 20 20 20 63 72  .}.else {.    cr
2f40: 65 61 74 65 5f 74 61 62 6c 65 28 29 3b 0a 20 20  eate_table();.  
2f50: 20 20 70 61 67 65 5f 68 74 6d 6c 28 22 0a 20 20    page_html(".  
2f60: 20 20 20 20 20 3c 68 33 3e 41 75 74 68 6f 72 69       <h3>Authori
2f70: 7a 61 74 69 6f 6e 20 65 6e 64 70 6f 69 6e 74 3c  zation endpoint<
2f80: 2f 68 33 3e 0a 20 20 20 20 20 20 20 54 68 65 72  /h3>.       Ther
2f90: 65 20 77 61 73 20 6e 6f 20 3f 63 6f 64 65 3d 20  e was no ?code= 
2fa0: 6f 72 20 3f 6d 65 3d 20 70 61 72 61 6d 65 74 65  or ?me= paramete
2fb0: 72 2c 3c 62 72 3e 20 73 6f 20 6e 6f 74 20 61 6e  r,<br> so not an
2fc0: 20 61 63 74 75 61 6c 20 49 6e 64 69 65 2f 4f 41   actual Indie/OA
2fd0: 75 74 68 20 72 65 71 75 65 73 74 2e 0a 20 20 20  uth request..   
2fe0: 20 20 20 20 3c 75 6c 3e 0a 20 20 20 20 20 20 20      <ul>.       
2ff0: 20 20 3c 6c 69 3e 54 68 65 20 3c 63 6f 64 65 3e    <li>The <code>
3000: 66 78 5f 61 75 74 68 3c 2f 63 6f 64 65 3e 20 74  fx_auth</code> t
3010: 61 62 6c 65 20 69 73 20 6e 6f 77 20 63 6f 6e 66  able is now conf
3020: 69 67 75 72 65 64 2e 0a 20 20 20 20 20 20 20 20  igured..        
3030: 20 3c 6c 69 3e 55 73 65 72 73 20 73 74 69 6c 6c   <li>Users still
3040: 20 6e 65 65 64 20 74 6f 20 61 64 64 20 74 68 65   need to add the
3050: 69 72 20 68 6f 6d 65 70 61 67 65 20 69 6e 20 74  ir homepage in t
3060: 68 65 20 63 6f 6e 74 61 63 74 2f 60 69 6e 66 6f  he contact/`info
3070: 60 20 66 69 65 6c 64 2e 20 28 53 65 65 20 3c 61  ` field. (See <a
3080: 20 68 72 65 66 3d 27 75 73 65 72 5f 63 6f 6e 66   href='user_conf
3090: 69 67 27 3e 65 78 74 2f 75 73 65 72 5f 63 6f 6e  ig'>ext/user_con
30a0: 66 69 67 3c 2f 61 3e 2e 29 0a 20 20 20 20 20 20  fig</a>.).      
30b0: 20 20 20 3c 6c 69 3e 41 6e 64 20 74 68 65 6e 20     <li>And then 
30c0: 64 65 63 6c 61 72 65 20 74 68 69 73 20 65 6e 64  declare this end
30d0: 70 6f 69 6e 74 20 6f 6e 20 74 68 65 69 72 20 70  point on their p
30e0: 65 72 73 6f 6e 61 6c 20 68 6f 6d 65 70 61 67 65  ersonal homepage
30f0: 3a 3c 62 72 3e 0a 20 20 20 20 20 20 20 20 20 20  :<br>.          
3100: 20 20 20 3c 63 6f 64 65 3e 26 6c 74 3b 6c 69 6e     <code>&lt;lin
3110: 6b 20 72 65 6c 3d 61 75 74 68 6f 72 69 7a 61 74  k rel=authorizat
3120: 69 6f 6e 5f 65 6e 64 70 6f 69 6e 74 20 68 72 65  ion_endpoint hre
3130: 66 3d 27 68 74 74 70 73 3a 2f 2f 24 5f 53 45 52  f='https://$_SER
3140: 56 45 52 5b 53 45 52 56 45 52 5f 4e 41 4d 45 5d  VER[SERVER_NAME]
3150: 24 5f 53 45 52 56 45 52 5b 50 48 50 5f 53 45 4c  $_SERVER[PHP_SEL
3160: 46 5d 27 26 67 74 3b 3c 2f 63 6f 64 65 3e 0a 20  F]'&gt;</code>. 
3170: 20 20 20 20 20 20 3c 2f 75 6c 3e 0a 20 20 20 20        </ul>.    
3180: 22 29 3b 0a 7d 0a 0a 0a 3f 3e                    ");.}...?>