From 1a2035330a0e7ee93af1e2927ca7ab1811119e8b Mon Sep 17 00:00:00 2001 From: Justin Campbell Date: Wed, 24 Mar 2021 00:23:07 -0400 Subject: [PATCH] Update auth docs to include fetch/curl examples for getting, passing and refreshing tokens --- docs/api/Authentication.md | 128 ++++++++++++++++++++++++++++++++++++- mkdocs.yml | 2 + 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/docs/api/Authentication.md b/docs/api/Authentication.md index 5436bf3..3c54581 100644 --- a/docs/api/Authentication.md +++ b/docs/api/Authentication.md @@ -2,6 +2,7 @@ The webqueue2 API uses a two stage authentication system combining Active Directory and [HTTP Token](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) (or "bearer") authentication. +## Getting an Access Token All API calls require an access token. You can get an access token by making a POST request to the `/api/login` endpoint containing the JSON encoded username and password of a valid user. ??? info "Who is a valid user?" @@ -52,6 +53,8 @@ All API calls require an access token. You can get an access token by making a P header = "Content-Type: application/json" data = {"username":"USERNAME","password":"PASSWORD"} url = "{{ production_url }}/api/login" + # Save cookies for future API calls + cookies = "wq_cookies" ``` Restrict read permissions to your user: @@ -65,7 +68,7 @@ All API calls require an access token. You can get an access token by making a P ``` ``` # Expected Output: - {"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTYxODI1MjMsIm5iZiI6MTYxNjE4MjUyMywianRpIjoiYzI2ZjY4NDctZGU2OC00ODUzLWI4ODctMTBmZDAyMDcyY2U0IiwiZXhwIjoxNjE2MTgzNDIzLCJzdWIiOiJjYW1wYjMwMyIsImZyZXNoIjpmYWxzZSwidHlwZSI6ImFjY2VzcyIsImNzcmYiOiJjYjYzZWE1My1jOWQ2LTQ5YTItYmZhMi0wY2U4N2Q3YzcxZDcifQ.T7bezsOMreMCXWR0R5w5BKI673hpOquCOnvT1XkyDjY"} + {"access_token": "{{ example_access_token }}"} ``` ??? tip "Tip for Parsing Tokens in Bash" @@ -73,4 +76,125 @@ All API calls require an access token. You can get an access token by making a P ```bash curl -K ~/wq_login.config | \ python3 -c "import sys, json; print(json.load(sys.stdin)['access_token'])" - ``` \ No newline at end of file + ``` + +## Making Calls With Access Token +To interact with the API, add an `Authorization` header to your request with a value of `Bearer TOKEN` where `TOKEN` is your access token. + +Example: Get item `ce100` +=== "fetch" + ```javascript + const ce100 = (async _ => { + const loginInit = { + method: "GET", + headers: {'Authentication': 'Bearer {{ example_access_token }}'}, + }; + + let apiResponse = await fetch("{{ production_url }}/api/data/ce/100", loginInit); + let data = await apiResponse.json(); + + if (data === null){ + return false; + } + + if (!apiResponse.ok){ + console.error(`Fetching item ce100 failed. Got code ${apiResponse.status} (${apiResponse.statusText})`); + return false; + } + + return data || false; + })(); + ``` + +=== "cURL" + Create a cURL config file named `wq_get_ce100.config`: + ```bash + nano ~/wq_get_ce100.config + ``` + + Set cURL options + ```python + # ~/wq_get_ce100.config + request = GET + header = "Authorization: Bearer {{ example_access_token }}" + url = "{{ production_url }}/api/data/ce/100" + ``` + + Run cURL using your new config file: + ```bash + curl -K ~/wq_get_ce100.config + ``` + ``` + # Expected Output: + { 'assignedTo': 'campb303', 'building': '', 'content': [...], 'dateReceived': '2020-06-23T13:25:51-0400', 'department': '', 'headers': [...], 'isLocked': 'ce 100 is locked by knewell using qvi', 'lastUpdated': '2020-12-01T19:53:00-0500', 'number': 100, 'priority': '', 'queue': 'ce', 'status': 'Dont Delete', 'subject': 'Beepboop', 'userAlias': 'campb303', 'userEmail': 'campb303@purdue.edu', 'userName': 'Justin Campbell' } + ``` + +## Refreshing Access Tokens +By default, access tokens expire 15 minutes after being issued and need refreshed. During login, you'll receive two cookies: + +Name | Value | Path | Expiration | SameSite +-- | -- | -- | -- | -- +`refresh_token_cookie` | Your refresh token. | `/api/tokens/refresh` | 30 Days | Yes +`csrf_refresh_token` | Additional verification data. (e.g. `{{ example_csrf_token }}`) | `/` | Session | Yes + +The `refresh_token_cookie` is used to generate a new access token and will be sent back to the server with every request automatically. The `csrf_refresh_token` is used to verify the `refresh_token_cookie` and needs sent back as an `X-CSRF-TOKEN` header. + +To refresh your access token, make a POST request to the `/api/tokens/refresh` endpoint with the value of the `csrf_refresh_token` cookies inside a `X-CSRF-TOKEN` header: + +=== "fetch" + ```javascript + const newAccessToken = (async _ => { + const csrf_refresh_token = some_func_to_get_csrf_token(); + + const refreshInit = { + method: "POST", + headers: {'X-CSRF-TOKEN': csrf_refresh_token}, + }; + + let refreshResponse = await fetch(`{{ production_url }}/api/tokens/refresh`, refreshInit); + let data = await refreshResponse.json(); + + if (data === null){ + return false; + } + if (!refreshResponse.ok){ + console.error(`Refresh failed. Got code ${refreshResponse.status} (${refreshResponse.statusText})`); + return false; + } + + return data.access_token || false; + })(); + ``` + +=== "cURL" + Create a cURL config file named `wq_refresh.config`: + ```bash + nano ~/wq_refresh.config + ``` + + Set cURL options + ```python + # ~/wq_refresh.config + request = POST + header = "X-CSRF-TOKEN: {{ example_csrf_token }}" + url = "{{ production_url }}/api/tokens/refresh" + # Pull cookies from login. + cookie-jar = "wq_cookies" + ``` + + Run cURL using your new config file: + ```bash + curl -K ~/wq_refresh.config + ``` + ``` + # Expected Output: + {"access_token": "{{ example_access_token }}"} + ``` + + ??? tip "Tip for Parsing Tokens in Bash" + You can parse the access token using Python like this: + ```bash + curl -K ~/wq_login.config | \ + python3 -c "import sys, json; print(json.load(sys.stdin)['access_token'])" + ``` + ``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index b7ee5b1..37596d7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -60,6 +60,8 @@ extra: link: https://github.itap.purdue.edu/ECN/webqueue2 name: webqueue2 on GitHub production_url: "https://engineering.purdue.edu/webqueue/webqueue2/build" + example_access_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTY1NTIyMDIsIm5iZiI6MTYxNjU1MjIwMiwianRpIjoiZDgyNGM1MWItM2JmNy00ZDUzLWE0YTgtY2VhZWQ5ZmVjNGYzIiwiZXhwIjoxNjE2NTUzMTAyLCJzdWIiOiJjYW1wYjMwMyIsImZyZXNoIjpmYWxzZSwidHlwZSI6ImFjY2VzcyIsImNzcmYiOiI1Yjk5NWQ5OS05YjIzLTQyMjYtYTc0OC1lMmQ5OTA4MDkzOTQifQ.6z7EReDfhPkBkuAMHEvDuMDV4wVbqrWSjQXdRyv_5hE" + example_csrf_token: "7b7c1ea8-f6bb-4204-99af-cd4124a69d89" extra_javascript: - https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/tablesort.min.js