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
'requests']) prettyprinter.install_extras([
Keycloak Magic Link Experiments
Testing https://github.com/p2-inc/keycloak-magic-link
= "https://localhost:8037/realms/test/"
kc_auth_server_base_url = {
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.
= settings['kc_token_endpoint']
callback_url = requests.post(
request
callback_url,=False,
verify= {
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'],
}
)= request.json()['access_token']
service_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",
}= requests.post(
request 'kc_magic_link_endpoint'],
settings[=False,
verify= {
headers 'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': f'Bearer {service_access_token}',
},=json.dumps(payload)
data
)= request.json()['link']
magic_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
= requests.get(
request
magic_link,=False,
verify
)= request.json()['args']['code']
auth_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',
}= requests.post(
request 'kc_token_endpoint'],
settings[=False,
verify=payload
data
)= request.json()['access_token']
access_token = request.json()['refresh_token'] 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
="""-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyWxR1kkyyIoIhns7Ki5XiQLl+buGHj0YZ+vN7ZNYtxhWKKDQkg587Aki6maacd6PXaUGCZmEE2ZU7JCcziZjK2g9f3ZyUOp3eGwKFZeT2Z3NwkwpYF3hVB0+tAPz5t06y4zo9HqAUHe0xr267g0jofJsSPcTq/4iVwvoBTw8aODQjy3+iCgBKCNnhIUamJwbzz5QbLZw5RgjLrmHMqWfQCB/MNEo0me3Tu8r2sBZVLhY8j3rSfdPMtMj88vXNA+M8rCgTeteZbwo4+3wyFrcP6Yr6u9mhIi3kH/MOneP3p7W6AClBNUyad5ovc38FlQGUMZ4Vp9jiRN/4MwZ7yBwhQIDAQAB\n-----END PUBLIC KEY-----"""
public_keytry:
=["RS256"], audience='account'))
pprint(jwt.decode(access_token, public_key, algorithmsexcept 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'
}