Add an example Jupyter Notebook to refresh a token a call a D4Science HTTP service
This commit is contained in:
commit
21d9907553
|
@ -0,0 +1,221 @@
|
||||||
|
{
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 0,
|
||||||
|
"metadata": {
|
||||||
|
"colab": {
|
||||||
|
"provenance": []
|
||||||
|
},
|
||||||
|
"kernelspec": {
|
||||||
|
"name": "python3",
|
||||||
|
"display_name": "Python 3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"name": "python"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"# How to issue a request to a D4Science service\n",
|
||||||
|
"\n",
|
||||||
|
"In order to issue a request to an HTTP service hosted on D4Science infrastructure, you need to have a valid *UMA OAuth ACCESS_TOKEN.*\n",
|
||||||
|
"\n",
|
||||||
|
"You can get one from the portlet avaiable on the Science Gateway where your VRE is deployed.\n",
|
||||||
|
"\n",
|
||||||
|
"However, this token has a limited duration and it needs to be refreshed through a longer lived *REFRESH_TOKEN*, that you can find from the same porlet.\n",
|
||||||
|
"\n",
|
||||||
|
"In the following colab, you can find an example on how to deal with the expiration of an Access Token from Python."
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "FyO3MvcJKH7e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"Let's start by importing some required libraries:\n",
|
||||||
|
"\n",
|
||||||
|
"> *Note: if you try to execute this code outside of Google Colab, you probably need to install `requests` and `pyjwt` with*\n",
|
||||||
|
"\n",
|
||||||
|
"> ```pip install requests pyjwt==2.8.0```\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "W1lMfjcFL0G4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"import requests\n",
|
||||||
|
"import jwt\n",
|
||||||
|
"from datetime import datetime"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "NhA77gG5L3ez"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"and configure some variabiles with the values copied from the Personal Web Token Portlet and the `service_url` you want to issue a request to."
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "nW6EsJgRL-pL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"ACCESS_TOKEN = \"<YOUR_PERSONAL_OAUTH_ACCESS_TOKEN>\" # Get your Personal Access (UMA) Token from the portlet\n",
|
||||||
|
"REFRESH_TOKEN = \"<YOUR_PERSONAL_OAUTH2_REFRESH_TOKEN>\" # Get your OAuth2 Refresh Token from the portlet\n",
|
||||||
|
"CLIENT_ID = \"sobigdata.d4science.org\" # Get from the Refresh parameters section of the porlet\n",
|
||||||
|
"SERVICE_URL = \"https://netme-sobigdata.d4science.org\" # Base URL of the service you want to get access\n",
|
||||||
|
"\n",
|
||||||
|
"refresh_token_url = \"https://accounts.d4science.org/auth/realms/d4science/protocol/openid-connect/token\" # Get from the Refresh token URL section of the porlet"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "_CjKMHaYMUAl"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"The `refresh_token` function is used to get a new `access token` by means of a `refresh token`"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "oESq-ZDcM1hy"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def refreshToken(rtoken):\n",
|
||||||
|
" if tokenIsExpired(rtoken):\n",
|
||||||
|
" print(f\"Refresh token has expired too {expirationTime(rtoken)}\")\n",
|
||||||
|
" exit(-1)\n",
|
||||||
|
" data = { \"grant_type\": \"refresh_token\", \"client_id\": CLIENT_ID, \"refresh_token\": rtoken }\n",
|
||||||
|
" response = requests.post(refresh_token_url, data=data)\n",
|
||||||
|
" r = response.json()\n",
|
||||||
|
" if response.status_code != 200:\n",
|
||||||
|
" # print(yaml.dump(r))\n",
|
||||||
|
" exit(-1)\n",
|
||||||
|
" return r['access_token']"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "luV7dSc_NDpH"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"Two other helper functions, `expirationTime` and `tokenIsExpired` can be used to get the expiration time of a given `token` and check if a `token` is expired or not:"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "SMRQG9_4NOj2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def expirationTime(token):\n",
|
||||||
|
" decoded = jwt.decode(jwt=token, options={\"verify_signature\": False})\n",
|
||||||
|
" return datetime.fromtimestamp(decoded['exp'])\n",
|
||||||
|
"\n",
|
||||||
|
"def tokenIsExpired(token):\n",
|
||||||
|
" return datetime.now() > expirationTime(token)"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "zXsgW1_ENcdd"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"Finally, the function `executeServiceRequest` will issue the HTTP request to the given service `path`. It will check if the `ACCESS_TOKEN` is expired and in that case it will refresh it. It will return back the response from the service"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "uMJuxCeONh-Z"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"def executeServiceRequest(path):\n",
|
||||||
|
" global ACCESS_TOKEN\n",
|
||||||
|
" if tokenIsExpired(ACCESS_TOKEN):\n",
|
||||||
|
" print(f\"ACCESS token expired in {expirationTime(ACCESS_TOKEN)}\")\n",
|
||||||
|
" print(\"Refreshing token...\")\n",
|
||||||
|
" ACCESS_TOKEN = refreshToken(REFRESH_TOKEN)\n",
|
||||||
|
" print(f\"new ACCESS token expiration {expirationTime(ACCESS_TOKEN)}\")\n",
|
||||||
|
"\n",
|
||||||
|
" print(f\"Call to Service {SERVICE_URL}{path}\")\n",
|
||||||
|
" response = requests.post(f\"{SERVICE_URL}{path}\", headers={\"Authorization\": f\"Bearer {ACCESS_TOKEN}\"})\n",
|
||||||
|
" r = response.json()\n",
|
||||||
|
" if response.status_code != 200:\n",
|
||||||
|
" print(f\"\\nResponse: {r}\")\n",
|
||||||
|
" else:\n",
|
||||||
|
" raise Exception(\"Unable Contact service\")\n",
|
||||||
|
" return r"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "88mYxCfDNyvh"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"source": [
|
||||||
|
"So let's try to use the NetME service, in particular the network generation feature, available at the `network_generation` path.\n",
|
||||||
|
"\n",
|
||||||
|
"So we just need to call the `executeServiceRequest` function with the correct path.\n",
|
||||||
|
"\n",
|
||||||
|
"**Please also add any other header or payload data the requested service requires**\n"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"id": "ZZjBzHY_OIMG"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"source": [
|
||||||
|
"response = executeServiceRequest('/network_generation')"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"colab": {
|
||||||
|
"base_uri": "https://localhost:8080/"
|
||||||
|
},
|
||||||
|
"id": "Eti_zQ4DO7Ox",
|
||||||
|
"outputId": "b570e687-d7eb-49f8-b3c9-745998fd2963"
|
||||||
|
},
|
||||||
|
"execution_count": null,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"output_type": "stream",
|
||||||
|
"name": "stdout",
|
||||||
|
"text": [
|
||||||
|
"ACCESS token expired in 2024-03-13 15:07:34\n",
|
||||||
|
"Refreshing token...\n",
|
||||||
|
"new ACCESS token expiration 2024-03-14 14:25:28\n",
|
||||||
|
"Call to Service https://netme-sobigdata.d4science.org/network_generation\n",
|
||||||
|
"\n",
|
||||||
|
"Response: {'detail': [{'type': 'missing', 'loc': ['body'], 'msg': 'Field required', 'input': None, 'url': 'https://errors.pydantic.dev/2.6/v/missing'}]}\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue