Comparison

mod_rest/example/rest.sh @ 5654:b3484a112300

mod_rest/rest.sh: Update to use httpie-oauth2 plugin This bash implementation of OAuth2/OIDC was growing to the point where it needed a massive refactor, which made me look into alternatives where I finally settled on implementing oauth2 in a plugin for HTTPie.
author Kim Alvefur <zash@zash.se>
date Sat, 26 Aug 2023 14:37:04 +0200
parent 5432:1c52efb6fd42
child 5655:acd2f397ce6b
comparison
equal deleted inserted replaced
5653:401356232e1b 5654:b3484a112300
3 # Copyright (c) Kim Alvefur 3 # Copyright (c) Kim Alvefur
4 # This file is MIT/X11 licensed. 4 # This file is MIT/X11 licensed.
5 5
6 # Dependencies: 6 # Dependencies:
7 # - https://httpie.io/ 7 # - https://httpie.io/
8 # - https://github.com/stedolan/jq 8 # - https://hg.sr.ht/~zash/httpie-oauth2
9 # - some sort of XDG 'open' command
10 9
11 # Settings 10 # Settings
12 HOST="" 11 HOST=""
13 DOMAIN="" 12 DOMAIN=""
14 13
15 AUTH_METHOD="session-read-only"
16 AUTH_ID="rest"
17
18 if [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/restrc" ]; then 14 if [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/restrc" ]; then
19 # Config file can contain the above settings 15 # Config file can contain the above settings
20 source "${XDG_CONFIG_HOME:-$HOME/.config}/restrc" 16 source "${XDG_CONFIG_HOME:-$HOME/.config}/restrc"
17
18 if [ -z "${SCOPE:-}" ]; then
19 SCOPE="openid xmpp"
20 fi
21 fi 21 fi
22 22
23 if [[ $# == 0 ]]; then 23 if [[ $# == 0 ]]; then
24 echo "${0##*/} [-h HOST] [-u USER|--login] [/path] kind=(message|presence|iq) ...." 24 echo "${0##*/} [-h HOST] [/path] kind=(message|presence|iq) ...."
25 # Last arguments are handed to HTTPie, so refer to its docs for further details 25 # Last arguments are handed to HTTPie, so refer to its docs for further details
26 exit 0 26 exit 0
27 fi 27 fi
28 28
29 if [[ "$1" == "-h" ]]; then 29 if [[ "$1" == "-h" ]]; then
43 else 43 else
44 HOST="$HOST.$DOMAIN" 44 HOST="$HOST.$DOMAIN"
45 fi 45 fi
46 fi 46 fi
47 47
48 if [[ "$1" == "-u" ]]; then
49 # -u username
50 AUTH_METHOD="auth"
51 AUTH_ID="$2"
52 shift 2
53 elif [[ "$1" == "-rw" ]]; then
54 # To e.g. save Accept headers to the session
55 AUTH_METHOD="session"
56 shift 1
57 fi
58
59 if [[ "$1" == "--login" ]]; then
60 shift 1
61
62 # Check cache for OAuth client
63 if [ -f "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST" ]; then
64 source "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST"
65 fi
66
67 OAUTH_META="$(http --check-status --json "https://$HOST/.well-known/oauth-authorization-server" Accept:application/json)"
68 AUTHORIZATION_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.authorization_endpoint')"
69 TOKEN_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.token_endpoint')"
70
71 if [ -z "${OAUTH_CLIENT_INFO:-}" ]; then
72 # Register a new OAuth client
73 REGISTRATION_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.registration_endpoint')"
74 OAUTH_CLIENT_INFO="$(http --check-status "$REGISTRATION_ENDPOINT" Content-Type:application/json Accept:application/json client_name=rest.sh client_uri="https://modules.prosody.im/mod_rest" application_type=native software_id=0bdb0eb9-18e8-43af-a7f6-bd26613374c0 redirect_uris:='["urn:ietf:wg:oauth:2.0:oob"]')"
75 mkdir -p "${XDG_CACHE_HOME:-$HOME/.cache}/rest/"
76 typeset -p OAUTH_CLIENT_INFO >> "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST"
77 fi
78
79 CLIENT_ID="$(echo "$OAUTH_CLIENT_INFO" | jq -e -r '.client_id')"
80 CLIENT_SECRET="$(echo "$OAUTH_CLIENT_INFO" | jq -e -r '.client_secret')"
81
82 if [ -n "${REFRESH_TOKEN:-}" ]; then
83 TOKEN_RESPONSE="$(http --check-status --form "$TOKEN_ENDPOINT" 'grant_type=refresh_token' "client_id=$CLIENT_ID" "client_secret=$CLIENT_SECRET" "refresh_token=$REFRESH_TOKEN")"
84 ACCESS_TOKEN="$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')"
85 if [ "$ACCESS_TOKEN" == "null" ]; then
86 ACCESS_TOKEN=""
87 fi
88 fi
89
90 if [ -z "${ACCESS_TOKEN:-}" ]; then
91 CODE_CHALLENGE="$(head -c 33 /dev/urandom | base64 | tr /+ _-)"
92 open "$AUTHORIZATION_ENDPOINT?response_type=code&client_id=$CLIENT_ID&code_challenge=$CODE_CHALLENGE&scope=${SCOPE:-openid+prosody:user}"
93 read -p "Paste authorization code: " -s -r AUTHORIZATION_CODE
94
95 TOKEN_RESPONSE="$(http --check-status --form "$TOKEN_ENDPOINT" 'grant_type=authorization_code' "client_id=$CLIENT_ID" "client_secret=$CLIENT_SECRET" "code=$AUTHORIZATION_CODE" code_verifier="$CODE_CHALLENGE")"
96 ACCESS_TOKEN="$(echo "$TOKEN_RESPONSE" | jq -e -r '.access_token')"
97 REFRESH_TOKEN="$(echo "$TOKEN_RESPONSE" | jq -r '.refresh_token')"
98
99 if [ "$REFRESH_TOKEN" != "null" ]; then
100 # FIXME Better type check would be nice, but nobody should ever have the
101 # string "null" as a legitimate refresh token...
102 typeset -p REFRESH_TOKEN >> "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST"
103 fi
104
105 if [ -n "${COLORTERM:-}" ]; then
106 echo -ne '\e[1K\e[G'
107 else
108 echo
109 fi
110 fi
111
112 USERINFO_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.userinfo_endpoint')"
113 http --check-status -b --session rest "$USERINFO_ENDPOINT" "Authorization:Bearer $ACCESS_TOKEN" Accept:application/json >&2
114 AUTH_METHOD="session-read-only"
115 AUTH_ID="rest"
116
117 elif [[ "$1" == "--logout" ]]; then
118 # Revoke token
119 source "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST"
120
121 OAUTH_META="$(http --check-status --json "https://$HOST/.well-known/oauth-authorization-server" Accept:application/json)"
122 REVOCATION_ENDPOINT="$(echo "$OAUTH_META" | jq -e -r '.revocation_endpoint')"
123
124 CLIENT_ID="$(echo "$OAUTH_CLIENT_INFO" | jq -e -r '.client_id')"
125 CLIENT_SECRET="$(echo "$OAUTH_CLIENT_INFO" | jq -e -r '.client_secret')"
126
127 http -h --check-status --auth "$CLIENT_ID:$CLIENT_SECRET" --form "$REVOCATION_ENDPOINT" token="$REFRESH_TOKEN"
128
129 # Overwrite the token
130 typeset -p OAUTH_CLIENT_INFO > "${XDG_CACHE_HOME:-$HOME/.cache}/rest/$HOST"
131 exit 0
132 fi
133
134 if [[ $# == 0 ]]; then
135 # Just login?
136 exit 0
137 fi
138 48
139 # For e.g /disco/example.com and such GET queries 49 # For e.g /disco/example.com and such GET queries
140 GET_PATH="" 50 GET_PATH=""
141 if [[ "$1" == /* ]]; then 51 if [[ "$1" == /* ]]; then
142 GET_PATH="$1" 52 GET_PATH="$1"
143 shift 1 53 shift 1
144 fi 54 fi
145 55
146 http --check-status -p b "--$AUTH_METHOD" "$AUTH_ID" "https://$HOST/rest$GET_PATH" "$@" 56 https --check-status -p b --session rest -A oauth2 -a "$HOST" --oauth2-scope "$SCOPE" "$HOST/rest$GET_PATH" "$@"