Keycloak Magic Link Experiments

Testing https://github.com/p2-inc/keycloak-magic-link
import base64
import hashlib
import html
import json
import os
import re
import urllib.parse
import requests
import uuid
import jwt
import cryptography
import prettyprinter
from prettyprinter import pprint
prettyprinter.install_extras(['requests'])
kc_auth_server_base_url = "https://localhost:8037/realms/test/"
settings = {
  'kc_auth_server_base_url'          : kc_auth_server_base_url,
  'kc_token_endpoint'                : kc_auth_server_base_url + "protocol/openid-connect/token",
  'kc_magic_link_endpoint'           : kc_auth_server_base_url + "magic-link/",
  'kc_service_client_secret'         : "MCa5A3oYVd5qI1QP0kGYl0YgB6HnhVCE",
  'kc_service_client_id'             : "realm-management",
  'kc_magic_link_client_id'          : "account-console",
  'kc_magic_link_expiration_seconds' : 600,
}
############################
# Get Service account token.
callback_url = settings['kc_token_endpoint']
request = requests.post(
    callback_url,
    verify=False,
    headers = {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    data = {
        'grant_type':    'client_credentials',
        'client_id':     settings['kc_service_client_id'],
        'client_secret': settings['kc_service_client_secret'],
    }
)
service_access_token = request.json()['access_token']
service_access_token
/Users/admin/Library/Caches/pypoetry/virtualenvs/explore-keycloak-hV1l5bXr-py3.9/lib/python3.9/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  warnings.warn(
'eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJCV2cxNGNrN2hsNkZMaUYzQlEwWHUydjBQM24yS2tNMlVBNG9xVnZpOXEwIn0.eyJleHAiOjE2NjQ1NTI0NDQsImlhdCI6MTY2NDU1MjE0NCwianRpIjoiMTBiNzNkMzktYWQ4ZC00YzI1LWI5ODAtNjNiZWQxN2YwNTRiIiwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODAzNy9yZWFsbXMvdGVzdCIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJjMmY1ZGE5ZS0wZjY0LTQzNDAtODQzNy0xMWMzZjMyYWJmZmEiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJyZWFsbS1tYW5hZ2VtZW50IiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLXRlc3QiLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsicmVhbG0tbWFuYWdlbWVudCI6eyJyb2xlcyI6WyJtYW5hZ2UtdXNlcnMiLCJ1bWFfcHJvdGVjdGlvbiIsInZpZXctdXNlcnMiLCJxdWVyeS1ncm91cHMiLCJxdWVyeS11c2VycyJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SWQiOiJyZWFsbS1tYW5hZ2VtZW50IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjbGllbnRIb3N0IjoiMTcyLjI1LjAuMSIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1yZWFsbS1tYW5hZ2VtZW50IiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yNS4wLjEifQ.I2DD78vSNdAN8WNSe24y0Q8eJyJKdI5UVY_-lL9_Q0DHrvCCq6rg7x0OqkpGguhlqBkULggNJaIWdSdPZNUA4LQWiHc9pEUdqEva5fwjPI0rsmLwY2kE17btRyVqL8GnxKJlhJyDqSkFRgRFmTPOnqW7bVbYVL_ASbVK-v8dq7v_Td89Gh3nqAOPcWFm58hhIwxa1kaMu9e2USvheLjCmxAhlmgETg0J5LMNhC3kYQ2FRUjwNb6k4kwez2nKBkF9bNcnFoPpvz3wf_KE4-nGkdggSpwt1JCHUjMgN2WDM9Al1zvoUGJHdY9Hwn2n9a-BjqK3nTHZlMEiH6ny0m7vIw'
######################
# Generate Magic Link.
payload = {
    'email':              'test_user@magiclink.test',
    'client_id':          settings['kc_magic_link_client_id'],
    'redirect_uri':       'https://httpbin.org/anything',
    'expiration_seconds': settings['kc_magic_link_expiration_seconds'],
    'force_create':       "true",
    'send_email':         "true",
    'update_profile':     "false",
}
request = requests.post(
    settings['kc_magic_link_endpoint'],
    verify=False,
    headers = {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': f'Bearer {service_access_token}',
    },
    data=json.dumps(payload)
)
magic_link = request.json()['link']
magic_link
/Users/admin/Library/Caches/pypoetry/virtualenvs/explore-keycloak-hV1l5bXr-py3.9/lib/python3.9/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  warnings.warn(
'https://localhost:8037/realms/test/login-actions/action-token?key=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1MzNiMmIxOS04NTliLTQwN2ItYjk2ZC1mZjVkOTM2NTZhNjAifQ.eyJleHAiOjE2NjQ1NTI3NDQsImlhdCI6MTY2NDU1MjE0NCwianRpIjoiMDZkYWVlOGItYjJhYS00ZTM3LTk0NjgtMTFjOTBlMGJiNzA2IiwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODAzNy9yZWFsbXMvdGVzdCIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjgwMzcvcmVhbG1zL3Rlc3QiLCJzdWIiOiJlZjJlNmM2OS03MjJmLTQ1MTAtYWViYi0zOTliNDA4NjljZGUiLCJ0eXAiOiJleHQtbWFnaWMtbGluayIsImF6cCI6ImFjY291bnQtY29uc29sZSIsIm5vbmNlIjoiMDZkYWVlOGItYjJhYS00ZTM3LTk0NjgtMTFjOTBlMGJiNzA2IiwicmR1IjoiaHR0cHM6Ly9odHRwYmluLm9yZy9hbnl0aGluZyJ9.FFB9BwPp-xNFgsWI6a2PJPiIqAJH4_EQcGAOSM2sRtY&client_id=account-console'
################################
# Follow Magic Link and get code
request = requests.get(
    magic_link,
    verify=False,
)
auth_code = request.json()['args']['code']
auth_code
/Users/admin/Library/Caches/pypoetry/virtualenvs/explore-keycloak-hV1l5bXr-py3.9/lib/python3.9/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  warnings.warn(
/Users/admin/Library/Caches/pypoetry/virtualenvs/explore-keycloak-hV1l5bXr-py3.9/lib/python3.9/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  warnings.warn(
/Users/admin/Library/Caches/pypoetry/virtualenvs/explore-keycloak-hV1l5bXr-py3.9/lib/python3.9/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'httpbin.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  warnings.warn(
'1ae64602-f917-493e-a3bc-19e3cefc9f0b.9f106b8a-dd46-49d3-81d0-2fba77539334.d728e743-f924-43a0-b831-b58fb9d9483b'
#################################
# Exchange code for token and JWT
payload = {
    'grant_type':         'authorization_code',
    'client_id':          settings['kc_magic_link_client_id'],
    'code':               auth_code,
    'redirect_uri':       'https://httpbin.org/anything',
}
request = requests.post(
    settings['kc_token_endpoint'],
    verify=False,
    data=payload
)
access_token = request.json()['access_token']
refresh_token = request.json()['refresh_token']
/Users/admin/Library/Caches/pypoetry/virtualenvs/explore-keycloak-hV1l5bXr-py3.9/lib/python3.9/site-packages/urllib3/connectionpool.py:1045: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
  warnings.warn(
print(access_token)
print()
print(refresh_token)
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJCV2cxNGNrN2hsNkZMaUYzQlEwWHUydjBQM24yS2tNMlVBNG9xVnZpOXEwIn0.eyJleHAiOjE2NjQ1NTI0NDUsImlhdCI6MTY2NDU1MjE0NSwiYXV0aF90aW1lIjoxNjY0NTUyMTQ0LCJqdGkiOiJjNTYzYTJhMy0zODMzLTRmZGEtYWEzNy05OGIzMWNmMjk3M2MiLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo4MDM3L3JlYWxtcy90ZXN0IiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImVmMmU2YzY5LTcyMmYtNDUxMC1hZWJiLTM5OWI0MDg2OWNkZSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFjY291bnQtY29uc29sZSIsInNlc3Npb25fc3RhdGUiOiI5ZjEwNmI4YS1kZDQ2LTQ5ZDMtODFkMC0yZmJhNzc1MzkzMzQiLCJhY3IiOiIxIiwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjlmMTA2YjhhLWRkNDYtNDlkMy04MWQwLTJmYmE3NzUzOTMzNCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0X3VzZXJAbWFnaWNsaW5rLnRlc3QiLCJlbWFpbCI6InRlc3RfdXNlckBtYWdpY2xpbmsudGVzdCJ9.cp90P07zXwTd2w4WCJXb2aMuafN2m64r2DVDu7tE2QDTB8Rk6LRwiWCaEAEIsD2hdMIZILxme55DVtJgZuyaLlw1mk4wgPlQIPL9FnosKKZ5pOub5RhPnBJfM-yfivInB2Nhl2MazXK37TCOjqoVGdJNZyta8rBNUKqXL0RAgrPl-ZzY7e-VEJjnxsqqxeuuLxjPHh1ONa4XVfZHAxO7LH7CZd78FtsFBdQMr-X5Pa3KkAtp2vSlESqcVKB6rYD484Sv6KFAeIVDaUoiy56CbO3N4NGEULlojoSjOhE2wwebz7xHMPSBNLQHfp85G-A5mYmST3o5x00-VoCTw-Odvw

eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1MzNiMmIxOS04NTliLTQwN2ItYjk2ZC1mZjVkOTM2NTZhNjAifQ.eyJleHAiOjE2NjQ1NTM5NDUsImlhdCI6MTY2NDU1MjE0NSwianRpIjoiNzM0Nzk0MDUtMWViNC00ZDEwLTllMGYtOTNlZjZlYTc4ZWI0IiwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODAzNy9yZWFsbXMvdGVzdCIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjgwMzcvcmVhbG1zL3Rlc3QiLCJzdWIiOiJlZjJlNmM2OS03MjJmLTQ1MTAtYWViYi0zOTliNDA4NjljZGUiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoiYWNjb3VudC1jb25zb2xlIiwic2Vzc2lvbl9zdGF0ZSI6IjlmMTA2YjhhLWRkNDYtNDlkMy04MWQwLTJmYmE3NzUzOTMzNCIsInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjlmMTA2YjhhLWRkNDYtNDlkMy04MWQwLTJmYmE3NzUzOTMzNCJ9.anbMKLAAYYEsOV3wQ-bbRJw9bAqwSpszv72xVLHaZ9w
import cryptography, jwt
from jwt import ExpiredSignatureError
public_key="""-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyWxR1kkyyIoIhns7Ki5XiQLl+buGHj0YZ+vN7ZNYtxhWKKDQkg587Aki6maacd6PXaUGCZmEE2ZU7JCcziZjK2g9f3ZyUOp3eGwKFZeT2Z3NwkwpYF3hVB0+tAPz5t06y4zo9HqAUHe0xr267g0jofJsSPcTq/4iVwvoBTw8aODQjy3+iCgBKCNnhIUamJwbzz5QbLZw5RgjLrmHMqWfQCB/MNEo0me3Tu8r2sBZVLhY8j3rSfdPMtMj88vXNA+M8rCgTeteZbwo4+3wyFrcP6Yr6u9mhIi3kH/MOneP3p7W6AClBNUyad5ovc38FlQGUMZ4Vp9jiRN/4MwZ7yBwhQIDAQAB\n-----END PUBLIC KEY-----"""
try:
  pprint(jwt.decode(access_token, public_key, algorithms=["RS256"], audience='account'))
except ExpiredSignatureError:
  print("Signature Expired")
{
    'exp': 1664552445,
    'iat': 1664552145,
    'auth_time': 1664552144,
    'jti': 'c563a2a3-3833-4fda-aa37-98b31cf2973c',
    'iss': 'https://localhost:8037/realms/test',
    'aud': 'account',
    'sub': 'ef2e6c69-722f-4510-aebb-399b40869cde',
    'typ': 'Bearer',
    'azp': 'account-console',
    'session_state': '9f106b8a-dd46-49d3-81d0-2fba77539334',
    'acr': '1',
    'resource_access': {
        'account': {'roles': ['manage-account', 'manage-account-links']}
    },
    'scope': 'email profile',
    'sid': '9f106b8a-dd46-49d3-81d0-2fba77539334',
    'email_verified': True,
    'preferred_username': 'test_user@magiclink.test',
    'email': 'test_user@magiclink.test'
}