verb icon indicating copy to clipboard operation
verb copied to clipboard

How auth can be added to the request, and reuse the token?

Open DevGiuDev opened this issue 1 year ago • 2 comments

Hi,

I'm struggling with this without success. Any sample I see related to verb or restclient use auth. I'm trying to migrate from Insomnia to eMacs with ORG+verb

I'm starting to the login API request. I have to do a POST request to http://localhost:8081/backend/rest/v2/oauth/token

The cUrl command is as follows:

curl --request POST \
  --url http://localhost:8081/backend/rest/v2/oauth/token \
  --header 'Authorization: Basic YXBwYnV5YmFja2VuZC1lcnZEVUdmUjo5YTZhMzUzMjMzZTA4MDgxZDcyMWJjZWM3NDkwOTNhZGU5MTcyZGZkNWM3ZmViYjgxMjc4OWM2ZGRkZmVhMzYy' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data username=admin \
  --data password=admin \
  --data grant_type=password

And this is what I have

* GetToken                                                             :verb:
#+begin_src verb
POST http://localhost:8081/backend/rest/v2/oauth/token
Authorization: Basic YXBwYnV5YmFja2VuZC1lcnZEVUdmUjo5YTZhMzUzMjMzZTA4MDgxZDcyMWJjZWM3NDkwOTNhZGU5MTcyZGZkNWM3ZmViYjgxMjc4OWM2ZGRkZmVhMzYy
Content-Type: application/x-www-form-urlencoded

username=admin
password=admin
grant_type=password    
#+end_src

But the API always tells me:

{
  "error": "invalid_request",
  "error_description": "Missing grant type"
}

and I expect to get something like:

{
	"access_token": "aYm2RV_bOK5LKEQRcXLqS-_a3_w",
	"token_type": "bearer",
	"refresh_token": "RoEHzx2MkjKUP91Ew5WJhtFHQZs",
	"expires_in": 43199,
	"scope": "rest-api"
}

Where I would like to get the access_token to use it in other requests as Bearer.

One thing I do on Insomnia, and I don't know if could be possible here, is that if I call another request, getToken will be called to request a token if necessary.

Thanks.

DevGiuDev avatar Jul 13 '24 14:07 DevGiuDev

I don't think it is possible to split the raw data into multiple lines like you have. When I copy that Verb block as curl (C-c C-e C-r c), it gives

 curl 'http://localhost:8081/backend/rest/v2/oauth/token' \
 -H 'Authorization: Basic YXBwYnV5YmFja2VuZC1lcnZEVUdmUjo5YTZhMzUzMjMzZTA4MDgxZDcyMWJjZWM3NDkwOTNhZGU5MTcyZGZkNWM3ZmViYjgxMjc4OWM2ZGRkZmVhMzYy' \
 -H 'Content-Type: application/x-www-form-urlencoded' \
 -X POST \
 --data-raw 'username=admin
 password=admin
 grant_type=password    '

You could join it all on one line using & as delimiters:

* GetToken                                                             :verb:
#+begin_src verb
POST http://localhost:8081/backend/rest/v2/oauth/token
Authorization: Basic YXBwYnV5YmFja2VuZC1lcnZEVUdmUjo5YTZhMzUzMjMzZTA4MDgxZDcyMWJjZWM3NDkwOTNhZGU5MTcyZGZkNWM3ZmViYjgxMjc4OWM2ZGRkZmVhMzYy
Content-Type: application/x-www-form-urlencoded

username=admin&password=admin&grant_type=password    
#+end_src

Or, more readably in my opinion, use an Elisp snippet:

* GetToken                                                             :verb:
 #+begin_src verb
 POST http://localhost:8081/backend/rest/v2/oauth/token
 Authorization: Basic YXBwYnV5YmFja2VuZC1lcnZEVUdmUjo5YTZhMzUzMjMzZTA4MDgxZDcyMWJjZWM3NDkwOTNhZGU5MTcyZGZkNWM3ZmViYjgxMjc4OWM2ZGRkZmVhMzYy
 Content-Type: application/x-www-form-urlencoded
 
 {{(mapconcat 'identity '("username=admin" "password=admin" "grant_type=password") "&")}}
 #+end_src

(But the snippet needs to be all on one line; you could probably break it up using a noweb reference if you have a lot more data to include.)

Either of these give a correct looking command when copied to curl:

curl 'http://localhost:8081/backend/rest/v2/oauth/token' \
 -H 'Authorization: Basic YXBwYnV5YmFja2VuZC1lcnZEVUdmUjo5YTZhMzUzMjMzZTA4MDgxZDcyMWJjZWM3NDkwOTNhZGU5MTcyZGZkNWM3ZmViYjgxMjc4OWM2ZGRkZmVhMzYy' \
 -H 'Content-Type: application/x-www-form-urlencoded' \
 -X POST \
 --data-raw 'username=admin&password=admin&grant_type=password'

armkeh avatar Aug 03 '24 17:08 armkeh

Regarding re-using the token, here is how I've been doing that mostly automatically:

This code block will let us extract elements from the JSON of a response:

#+name: extract-http-response-json
#+begin_src emacs-lisp
;; Search for the JSON contents by looking for a pair of curly braces;
;; if unexpected behaviour occurs, check if the response has extra braces, and if so, this might need to be made smarter
(let ((resp-json (if (string-match "{[^b-a]*}" response) ;; Silly regexp to match all characters including newline (`b-a` is an empty range, so ^-ing it matches all characters)
                     (match-string 0 response))))
  (apply 'verb-json-get resp-json token-path))
#+end_src

Then this code block uses the above to extract the access and refresh tokens specifically:

#+name: extract-auth-tokens
#+begin_src emacs-lisp :var response="" :noweb yes :noweb-prefix no
(verb-var auth-token "") ;; Verb variable must be set before using verb-set-var, so set it to a default "" if not set yet
(let ((token-path '("access_token")))
  (verb-set-var "auth-token" <<extract-http-response-json>>))

(verb-var refresh-token "")
(let ((token-path '("refresh_token")))
  (verb-set-var "refresh-token" <<extract-http-response-json>>))

;; Echo the raw response for the results block, followed by info on the extracted values
(concat response "\n\n" (format "extracted keycloak-auth-token: %s\nextracted keycloak-refresh-token: %s"
  (verb-var auth-token) (verb-var refresh-token)))
#+end_src

Add the below :post header argument to your token request to use the above to extract the tokens:

#+begin_src verb :wrap src ob-verb-response :post extract-auth-tokens(*this*)
POST http://localhost:8081/backend/rest/v2/oauth/token
Content-Type: application/x-www-form-urlencoded
 
{{(mapconcat 'identity '("username=admin" "password=admin" "grant_type=password") "&")}}
#+end_src

And then you can use the auth token in all your requests by setting it in a top-level heading, and putting all requests that require authentication under that top-level heading:

* My API                                      :verb:
template http://localhost:8081/backend/rest/v2
Authorization: Bearer {{(verb-var auth-token)}}

** Request 1

...

** Request 2

...

armkeh avatar Aug 03 '24 17:08 armkeh

I don't think there's anything to add to this issue, I will leave the following thoughts:

  • The x-www-form-urlencoded encoding is just a way of encoding information in the body, and can be achieved using custom Elisp code as @armkeh built in their first comment. I may add a helper function to Verb to make this easier. But otherwise, there is nothing Verb-specific about how this is done.
  • I have added Verb-Map-Response recently which might make easier the extracting of data from responses received. Otherwise, it is still possible to use Verb-Store + (verb-stored-response ...) to fetch data from previously received responses.

federicotdn avatar Jan 05 '25 14:01 federicotdn

Added a helper function for use with x-www-form-urlencoded: https://github.com/federicotdn/verb/commit/ea789bfc05b0e08b670f035c2cd4fc394b5a4f8c

federicotdn avatar Jan 05 '25 18:01 federicotdn