test-challenger-revisit.sh (6274B)
1 #!/bin/bash 2 # This file is in the public domain. 3 # 4 # Tests that re-submitting to /challenge for an already-solved 5 # validation returns a redirect_url with both ``code`` and ``state`` 6 # query parameters, so the user agent can still be sent back to the 7 # OAuth client with a usable authorization grant. 8 # 9 # Regression test: previously the "already solved" branch returned the 10 # bare client redirect URI without code/state. 11 12 set -eu 13 14 function exit_skip() { 15 echo " SKIP: $1" 16 exit 77 17 } 18 19 function exit_fail() { 20 echo " FAIL: $@" 21 exit 1 22 } 23 24 function cleanup() 25 { 26 for n in $(jobs -p) 27 do 28 kill $n 2> /dev/null || true 29 done 30 rm -f "$LAST_RESPONSE" "$FILENAME" 31 wait 32 } 33 34 LAST_RESPONSE=$(mktemp responseXXXXXX.log) 35 FILENAME="test-challenger-revisit.txt" 36 37 trap cleanup EXIT 38 39 export PATH="$PATH:." 40 41 echo -n "Testing for jq" 42 jq -h > /dev/null || exit_skip "jq required" 43 echo " FOUND" 44 echo -n "Testing for curl" 45 curl -h > /dev/null || exit_skip "curl required" 46 echo " FOUND" 47 echo -n "Testing for wget" 48 wget -h > /dev/null || exit_skip "wget required" 49 echo " FOUND" 50 echo -n "Testing for challenger-httpd ..." 51 challenger-httpd -h > /dev/null || exit_skip "challenger-httpd required" 52 echo " FOUND" 53 54 CONF="test-challenger.conf" 55 BURL="http://localhost:9967" 56 REDIRECT_URI="http://client.example.com/" 57 58 echo -n "Initialize challenger database ..." 59 challenger-dbinit -r -c "${CONF}" &> dbinit.log 60 echo " OK" 61 62 echo -n "Add challenger client ..." 63 CLIENT_SECRET="secret-token:secret" 64 challenger-admin -c "${CONF}" -a "${CLIENT_SECRET}" "${REDIRECT_URI}" &> admin.log 65 echo " OK" 66 CLIENT_ID=1 67 68 echo -n "Start challenger-httpd ..." 69 challenger-httpd -L INFO -c "${CONF}" &> httpd.log & 70 71 for n in $(seq 1 50) 72 do 73 echo -n "." 74 sleep 0.2 75 OK=0 76 wget --tries=1 --timeout=1 "${BURL}/config" -o /dev/null -O /dev/null >/dev/null || continue 77 OK=1 78 break 79 done 80 if [ 1 != $OK ] 81 then 82 exit_skip "Failed to launch challenger service" 83 fi 84 85 86 echo -n "Setup new validation process..." 87 STATUS=$(curl "${BURL}/setup/${CLIENT_ID}" \ 88 -H "Authorization: Bearer ${CLIENT_SECRET}" \ 89 -d '' \ 90 -w "%{http_code}" -s -o $LAST_RESPONSE) 91 92 if [ "$STATUS" != "200" ] 93 then 94 exit_fail "Expected 200 OK. Got: $STATUS" $(cat $LAST_RESPONSE) 95 fi 96 NONCE=$(jq -r .nonce < "$LAST_RESPONSE") 97 echo " OK" 98 99 CLIENT_STATE="the-client-state" 100 CLIENT_SCOPE="the-client-scope" 101 102 echo -n "Initiating user login..." 103 STATUS=$(curl "${BURL}/authorize/${NONCE}" \ 104 -G \ 105 -H "Accept: application/json" \ 106 --data-urlencode "response_type=code" \ 107 --data-urlencode "client_id=${CLIENT_ID}" \ 108 --data-urlencode "redirect_uri=${REDIRECT_URI}" \ 109 --data-urlencode "state=${CLIENT_STATE}" \ 110 --data-urlencode "scope=${CLIENT_SCOPE}" \ 111 -w "%{http_code}" -s -o $LAST_RESPONSE) 112 113 if [ "$STATUS" != "200" ] 114 then 115 exit_fail "Expected 200 OK. Got: $STATUS" $(cat $LAST_RESPONSE) 116 fi 117 echo "OK" 118 119 120 echo -n "Initiating address submission..." 121 STATUS=$(curl "${BURL}/challenge/${NONCE}" \ 122 -X POST \ 123 -H "Accept: application/json" \ 124 --data-urlencode "filename=${FILENAME}" \ 125 -w "%{http_code}" -s -o $LAST_RESPONSE) 126 127 if [ "$STATUS" != "200" ] 128 then 129 exit_fail "Expected 200 OK. Got: $STATUS" $(cat $LAST_RESPONSE) 130 fi 131 echo "OK" 132 133 PIN=$(cat ${FILENAME} | awk '{print $5}') 134 135 echo -n "Initiating PIN ${PIN} submission..." 136 RESULT=$(curl "${BURL}/solve/${NONCE}" \ 137 -X POST \ 138 -H "Accept: text/html" \ 139 --data-urlencode "pin=${PIN}" \ 140 -w "%{http_code} %{redirect_url}" -s -o $LAST_RESPONSE) 141 STATUS=$(echo "$RESULT" | awk '{print $1}') 142 TARGET=$(echo "$RESULT" | awk '{print $2}') 143 144 if [ "$STATUS" != "302" ] 145 then 146 exit_fail "Expected 302. Got: $STATUS" $(cat $LAST_RESPONSE) 147 fi 148 149 # This is where we start to diverge from test-challenger.sh: 150 # We track the original (good) response and then check we get 151 # it again later. 152 153 ORIG_CODE=$(echo "$TARGET" | sed -e "s/.*?code=//g" -e "s/&.*//g") 154 ORIG_STATE=$(echo "$TARGET" | sed -e "s/.*&state=//g") 155 156 if [ -z "${ORIG_CODE}" ] 157 then 158 exit_fail "No code returned from /solve" 159 fi 160 echo "OK" 161 162 # Now the actual regression test: re-submit the SAME address to 163 # /challenge. Since the validation is already solved, the server 164 # should reply with a JSON ChallengeRedirect whose redirect_url 165 # carries an OAuth ``code`` (and the original ``state``) so the user 166 # agent can still be returned to the client with a valid grant. 167 echo -n "Re-submitting address after solve..." 168 STATUS=$(curl "${BURL}/challenge/${NONCE}" \ 169 -X POST \ 170 -H "Accept: application/json" \ 171 --data-urlencode "filename=${FILENAME}" \ 172 -w "%{http_code}" -s -o $LAST_RESPONSE) 173 174 if [ "$STATUS" != "200" ] 175 then 176 exit_fail "Expected 200 OK on re-submit. Got: $STATUS" $(cat $LAST_RESPONSE) 177 fi 178 179 TYPE=$(jq -r .type < "$LAST_RESPONSE") 180 if [ "$TYPE" != "completed" ] 181 then 182 exit_fail "Expected type=completed on re-submit. Got: $TYPE" $(cat $LAST_RESPONSE) 183 fi 184 185 REDIRECT_URL=$(jq -r .redirect_url < "$LAST_RESPONSE") 186 if [ -z "${REDIRECT_URL}" ] || [ "${REDIRECT_URL}" = "null" ] 187 then 188 exit_fail "Missing redirect_url in re-submit response" $(cat $LAST_RESPONSE) 189 fi 190 191 # redirect_url must have a query string with code= and state=. 192 case "${REDIRECT_URL}" in 193 *\?*code=*) 194 # Good! 195 ;; 196 *) 197 exit_fail "redirect_url has no 'code' parameter: ${REDIRECT_URL}" 198 ;; 199 esac 200 case "${REDIRECT_URL}" in 201 *state=*) 202 # Good! 203 ;; 204 *) 205 exit_fail "redirect_url has no 'state' parameter: ${REDIRECT_URL}" 206 ;; 207 esac 208 209 REVISIT_URL=$(echo "${REDIRECT_URL}" | sed -e "s/?.*//g") 210 REVISIT_CODE=$(echo "${REDIRECT_URL}" | sed -e "s/.*?code=//g" -e "s/&.*//g") 211 REVISIT_STATE=$(echo "${REDIRECT_URL}" | sed -e "s/.*&state=//g") 212 213 if [ "${REVISIT_URL}" != "${REDIRECT_URI}" ] 214 then 215 exit_fail "Re-submit redirect URL ${REVISIT_URL} differs from registered ${REDIRECT_URI}" 216 fi 217 if [ "${REVISIT_STATE}" != "${CLIENT_STATE}" ] 218 then 219 exit_fail "Re-submit state ${REVISIT_STATE} differs from original ${CLIENT_STATE}" 220 fi 221 # The code is deterministic over (nonce, secret, scope, address, 222 # redirect_uri), so re-issuing must produce the same value as /solve did. 223 if [ "${REVISIT_CODE}" != "${ORIG_CODE}" ] 224 then 225 exit_fail "Re-submit code ${REVISIT_CODE} differs from /solve code ${ORIG_CODE}" 226 fi 227 echo "OK" 228 229 exit 0