{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5946444c",
   "metadata": {},
   "source": [
    "# Goal\n",
    "\n",
    "Use the Auth0 SDK to create and delete M2M clients.\n",
    "\n",
    "## Python SDK\n",
    "* https://github.com/auth0/auth0-python\n",
    "\n",
    "## Auth0 management api docs\n",
    "* https://auth0.com/docs/api/management/v2\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "675728ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "from auth0.authentication import GetToken\n",
    "from auth0.management import Auth0, Clients, ClientGrants\n",
    "from environs import Env\n",
    "import sqlite3\n",
    "import time\n",
    "import copy\n",
    "import math\n",
    "import uuid\n",
    "import json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8b24a6c2-1537-45ec-b6fc-e4deae7f0286",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('dev-orchard.auth0.com', 'ntFRrLTUI5ouXJV5xKnQNDrlVqLVQTOU')"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Copy .env.shadow to .env\n",
    "# Update entries....\n",
    "\n",
    "env = Env()\n",
    "env.read_env('/Users/calh005/work/collab/technicalelvis/m2m_auth0/notebooks/.env', recurse=False)\n",
    "env('AUTH0_DOMAIN'), env('AUTH0_CLI_MACHINE_CLIENT_ID')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8a0f0e2-ff2a-4b52-9fca-78b7172a054e",
   "metadata": {},
   "source": [
    "# Auth0 Management Token "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "37837ed7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "mgmt_api_token: eyJhbGciOiJSUzI1NiIs...\n"
     ]
    }
   ],
   "source": [
    "def get_mgmt_token():\n",
    "    domain = env('AUTH0_DOMAIN')\n",
    "    non_interactive_client_id = env('AUTH0_CLI_MACHINE_CLIENT_ID')\n",
    "    non_interactive_client_secret = env('AUTH0_CLI_MACHINE_CLIENT_SECRET')\n",
    "    \n",
    "    get_token = GetToken(domain, non_interactive_client_id, client_secret=non_interactive_client_secret)\n",
    "    token = get_token.client_credentials('https://{}/api/v2/'.format(domain))\n",
    "    return token['access_token']\n",
    "\n",
    "mgmt_api_token = get_mgmt_token()\n",
    "print(f\"mgmt_api_token: {mgmt_api_token[:20]}...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d75e885-b3ea-4037-a9f8-168c40bc072f",
   "metadata": {},
   "source": [
    "# Auth0 Management ClientAPI \n",
    "\n",
    "https://github.com/auth0/auth0-python/blob/a31c62b85c8654259da0acb67517a3130120595c/auth0/management/auth0.py#L55\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "34ca44c6-f5e9-465a-ab11-6b0db9edd4ce",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<auth0.management.clients.Clients at 0x1043471f0>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def get_auth0_client(mgmt_api_token):\n",
    "    domain = env('AUTH0_DOMAIN')\n",
    "    \n",
    "    return Clients(domain, mgmt_api_token)\n",
    "\n",
    "\n",
    "auth0_client = get_auth0_client(mgmt_api_token)\n",
    "auth0_client"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30bb2171-8516-4ee6-96d3-a681ed7f8f7c",
   "metadata": {},
   "source": [
    "# Get a client\n",
    "\n",
    "Retrieve client details. A list of fields to include or exclude may also be specified. Note:\n",
    "\n",
    "* https://auth0.com/docs/api/management/v2/clients/get-clients-by-id\n",
    "* https://github.com/auth0/auth0-python/blob/a31c62b85c8654259da0acb67517a3130120595c/auth0/management/clients.py#L107"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "153b5ef8-2804-4dce-bd13-14d8ebbc0c30",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "is_token_endpoint_ip_header_trusted False\n",
      "name a0-m2m-app-deploy-client\n",
      "is_first_party True\n",
      "oidc_conformant True\n",
      "sso_disabled False\n",
      "cross_origin_auth False\n",
      "description \n",
      "logo_uri \n",
      "sso \n",
      "callbacks []\n",
      "allowed_logout_urls \n",
      "allowed_clients []\n",
      "allowed_origins \n",
      "jwt_configuration {'alg': 'RS256', 'lifetime_in_seconds': 36000, 'secret_encoded': False}\n",
      "token_endpoint_auth_method client_secret_post\n",
      "app_type non_interactive\n",
      "grant_types ['client_credentials']\n",
      "web_origins \n",
      "custom_login_page_on True\n"
     ]
    }
   ],
   "source": [
    "def get_client(client_id):\n",
    "    \"Get client json info for 'a0-m2m-app-deploy-client'\"\n",
    "    need_fields = [\n",
    "        \"is_token_endpoint_ip_header_trusted\",\n",
    "        \"name\",\n",
    "        \"is_first_party\",\n",
    "        \"oidc_conformant\",\n",
    "        \"sso_disabled\",\n",
    "        \"cross_origin_auth\",\n",
    "        \"description\",\n",
    "        \"logo_uri\",\n",
    "        \"sso\",\n",
    "        \"callbacks\",\n",
    "        \"allowed_logout_urls\",\n",
    "        \"allowed_clients\",\n",
    "        \"allowed_origins\",\n",
    "        \"jwt_configuration\",\n",
    "        \"token_endpoint_auth_method\",\n",
    "        \"app_type\",\n",
    "        \"grant_types\",\n",
    "        \"web_origins\",\n",
    "        \"custom_login_page_on\",\n",
    "    ]\n",
    "    response = auth0_client.get(id=client_id)\n",
    "    output = {}\n",
    "    for field in need_fields:\n",
    "        print(f\"\\t{field} {response.get(field, '')}\")\n",
    "\n",
    "    return response\n",
    "\n",
    "_ = get_client(env('AUTH0_CLI_MACHINE_CLIENT_ID'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ede02c9d-0546-424e-a5d2-2d9480226cad",
   "metadata": {},
   "source": [
    "# Get client grants\n",
    "\n",
    "* https://auth0.com/docs/api/management/v2/client-grants/get-client-grants\n",
    "* https://github.com/auth0/auth0-python/blob/a31c62b85c8654259da0acb67517a3130120595c/auth0/management/client_grants.py#L55"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "0a4fa850-7c3b-4aeb-9843-90485ba4693d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      "  {\n",
      "    \"id\": \"cgr_53odGbbJexLZUZ5m\",\n",
      "    \"client_id\": \"ntFRrLTUI5ouXJV5xKnQNDrlVqLVQTOU\",\n",
      "    \"audience\": \"https://dev-orchard.auth0.com/api/v2/\",\n",
      "    \"scope\": [\n",
      "      \"read:client_grants\",\n",
      "      \"read:clients\",\n",
      "      \"delete:clients\",\n",
      "      \"create:clients\",\n",
      "      \"read:client_keys\",\n",
      "      \"read:client_credentials\"\n",
      "    ]\n",
      "  }\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "def get_client_grants(client_id):\n",
    "    domain = env('AUTH0_DOMAIN')    \n",
    "    grants_api = ClientGrants(domain, mgmt_api_token)\n",
    "    return grants_api.all(client_id=client_id)\n",
    "\n",
    "\n",
    "print(json.dumps(get_client_grants(env('AUTH0_CLI_MACHINE_CLIENT_ID')), indent=2))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c7160378-86a0-478e-a9f9-5766dc82df3f",
   "metadata": {},
   "source": [
    "# Get Clients\n",
    "\n",
    "* https://auth0.com/docs/api/management/v2/clients/get-clients#response-messages\n",
    "* https://github.com/auth0/auth0-python/blob/a31c62b85c8654259da0acb67517a3130120595c/auth0/management/clients.py#L55"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7deffc06-4259-4208-845f-da7366a8bc24",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "def to_client_dict_entry(clients_list):\n",
    "    out_di = {}\n",
    "    for _client in clients_list:\n",
    "        k = _client[\"name\"]\n",
    "        out_di[k] = _client\n",
    "\n",
    "    return out_di\n",
    "\n",
    "def get_clients(page_size = 100, debug=False):\n",
    "    client_dict = {}\n",
    "    requested_app_fields = [\"name\", \"client_id\", \"client_metadata\"]\n",
    "    response = auth0_client.all(\n",
    "        fields=requested_app_fields,   # NOTE: Could also add 'client_secret'\n",
    "        page=0, \n",
    "        per_page=page_size,\n",
    "        extra_params = {\n",
    "            \"include_totals\": 'true',\n",
    "            \"app_type\": \"non_interactive\"\n",
    "        }\n",
    "    )\n",
    "\n",
    "    total = response['total']\n",
    "    total_pages = int(math.ceil(total / page_size))\n",
    "\n",
    "\n",
    "    if debug:\n",
    "        print(f\"total: {total}, total_pages: {total_pages}, page_size={page_size}\")\n",
    "        print(f\"Page 0: {[c['name'] for c in response['clients']]}\")\n",
    "\n",
    "    client_dict.update(\n",
    "        to_client_dict_entry(response[\"clients\"])\n",
    "    )\n",
    "    \n",
    "    for nextpage in range(1, total_pages):\n",
    "        # Calling GET too frequently causes a 429 error. So sleeeeeeep.\n",
    "        time.sleep(1.0)\n",
    "        response = auth0_client.all(\n",
    "        fields=requested_app_fields, \n",
    "        page=nextpage, \n",
    "        per_page=page_size,\n",
    "        extra_params = {\n",
    "                \"include_totals\": 'true',\n",
    "                \"app_type\": \"non_interactive\"\n",
    "            }\n",
    "        )\n",
    "        if debug:\n",
    "            print(f\"Page {nextpage}: {[c['name'] for c in response['clients']]}\")\n",
    "        client_dict.update(\n",
    "            to_client_dict_entry(response[\"clients\"])\n",
    "        )\n",
    "                   \n",
    "\n",
    "    return client_dict\n",
    "\n",
    "\n",
    "deployed_clients_dict = get_clients(page_size = 100, debug=False)\n",
    "print(\"\")\n",
    "print(f\"Found {len(deployed_clients_dict)} unique client names\")\n",
    "deployed_clients_dict"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "493f025b-fa62-4535-94ec-0dc13c99b206",
   "metadata": {},
   "source": [
    "# Create a new client\n",
    "\n",
    "\n",
    "* https://github.com/auth0/auth0-python/blob/master/auth0/management/clients.py#L96-L105\n",
    "* https://auth0.com/docs/api/v2#!/Clients/post_clients"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "099e4209-c6ad-4d6e-8e48-cc0588f8ebd0",
   "metadata": {},
   "outputs": [],
   "source": [
    "post_client_body = {\n",
    "    \"is_token_endpoint_ip_header_trusted\": False,\n",
    "    \"name\": \"telvis-test-01-machine-to-machine\",\n",
    "    \"is_first_party\": True,\n",
    "    \"oidc_conformant\": True,\n",
    "    \"sso_disabled\": False,\n",
    "    \"cross_origin_auth\": False,\n",
    "    \"description\": \"Test machine-to-machine client_credentials example\",\n",
    "    \"logo_uri\": \"\",\n",
    "    \"sso\": False,\n",
    "    \"callbacks\": [],\n",
    "    \"allowed_logout_urls\": [],\n",
    "    \"allowed_clients\": [],\n",
    "    \"allowed_origins\": [],\n",
    "    \"jwt_configuration\": {\n",
    "        \"alg\": \"RS256\",\n",
    "        \"lifetime_in_seconds\": 36000,\n",
    "        \"secret_encoded\": False\n",
    "    },\n",
    "    \"token_endpoint_auth_method\": \"client_secret_post\",\n",
    "    \"app_type\": \"non_interactive\",\n",
    "    \"grant_types\": [\n",
    "        \"authorization_code\",\n",
    "        \"implicit\",\n",
    "        \"refresh_token\",\n",
    "        \"client_credentials\"\n",
    "    ],\n",
    "    \"web_origins\": [],\n",
    "    \"custom_login_page_on\": True,\n",
    "    \"client_metadata\": {\n",
    "        \"m2m_identity_uuid\": str(uuid.uuid4())\n",
    "    }\n",
    "}\n",
    "\n",
    "def create_client(name):\n",
    "    _deployed_clients_dict = get_clients()\n",
    "\n",
    "    # post-clients does not support a uniqueness constraint on application name.\n",
    "    # Fetch the list of deployed clients and check if the name already exists.\n",
    "    if name in _deployed_clients_dict:\n",
    "        print(f\"[create_client] Application with name already exists: {name}, {_deployed_clients_dict[name]}\")\n",
    "        return\n",
    "    \n",
    "    body = copy.deepcopy(post_client_body)\n",
    "    body[\"name\"] = name\n",
    "    response = auth0_client.create(body=body)\n",
    "    return response\n",
    "    \n",
    "\n",
    "# _client = create_client(name=\"telvis-test-01-machine-to-machine\")\n",
    "# if _client:\n",
    "#     print(\"\")\n",
    "#     print(f\"Created name={_client['name']}, client_id={_client['client_id']}, metadata='{_client.get('client_metadata', {})}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d23ffe9e-b01a-442c-9c94-b14942b4c808",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "3cd697d1-7335-41c8-892a-293a1097230d",
   "metadata": {},
   "source": [
    "# Delete a client\n",
    "\n",
    "* See: https://auth0.com/docs/api/management/v2#!/Clients/delete_clients_by_id\n",
    "* https://github.com/auth0/auth0-python/blob/a31c62b85c8654259da0acb67517a3130120595c/auth0/management/clients.py#L135"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d29430b1-ec66-4904-a0fd-15725959ca84",
   "metadata": {},
   "outputs": [],
   "source": [
    "def delete_client(name):\n",
    "    _deployed_clients_dict = get_clients()\n",
    "    if name not in _deployed_clients_dict:\n",
    "        print(\"*****\")\n",
    "        print(f\"Application with name does not exist: {name}\")\n",
    "        return\n",
    "    \n",
    "    client_id = _deployed_clients_dict[name]['client_id']\n",
    "    print(f\"Attempting to delete name: '{name}' id: '{client_id}'\")\n",
    "    response = auth0_client.delete(id=client_id)\n",
    "    print(f\"Delete succeeded for: {name}\")\n",
    "    return response\n",
    "\n",
    "# for i in range(10):\n",
    "#     delete_client(name=\"telvis-test-01-machine-to-machine\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37dd8149-c747-411d-af93-e641f0dfca4c",
   "metadata": {},
   "source": [
    "# Create many clients"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2a1a0ecb-e34e-47ae-8359-595047ebec88",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "def create_many_clients(num_clients=10):\n",
    "    for i in range(num_clients):\n",
    "        name = f\"telvis-test-{i:03d}-machine-to-machine\"\n",
    "        _client = create_client(name=name)\n",
    "        time.sleep(1)\n",
    "\n",
    "        if _client:\n",
    "            print(f\"Created name={_client['name']}, client_id={_client['client_id']}, metadata='{_client.get('client_metadata', {})}\")\n",
    "\n",
    "create_many_clients(num_clients=500)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c565e059-9fbd-4990-899c-4f4e3a1a9718",
   "metadata": {},
   "source": [
    "# Delete many clients"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d1c6a12-71d5-4334-ad9f-3df12683655b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def delete_many_clients(num_clients=10, start=0):\n",
    "    for i in range(start, num_clients):\n",
    "        name = f\"telvis-test-{i:03d}-machine-to-machine\"\n",
    "        delete_client(name=name)\n",
    "        time.sleep(1)\n",
    "\n",
    "delete_many_clients(start=435, num_clients=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0671ae57-d5d3-45bb-afca-0b4a6d5b401d",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
