-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpkce_examples.py
More file actions
98 lines (71 loc) · 2.67 KB
/
Copy pathpkce_examples.py
File metadata and controls
98 lines (71 loc) · 2.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import argparse
import base64
import hashlib
import secrets
def generate_code_verifier() -> str:
"""
Generate a code verifier for PKCE (Proof Key for Code Exchange).
Returns:
str: A base64 URL-safe encoded string that serves as the code verifier.
"""
return (
base64.urlsafe_b64encode(secrets.token_bytes(64)).rstrip(b"=").decode("utf-8")
)
def generate_code_challenge(code_verifier: str) -> str:
"""
Generate a code challenge from the code verifier using SHA-256.
Args:
code_verifier (str): The code verifier string.
Returns:
str: A base64 URL-safe encoded string that serves as the code challenge.
"""
if not code_verifier:
raise ValueError("Code verifier must not be empty.")
code_challenge: bytes = hashlib.sha256(code_verifier.encode("utf-8")).digest()
return base64.urlsafe_b64encode(code_challenge).rstrip(b"=").decode("utf-8")
def generate_state() -> str:
"""
Generate a random state string for CSRF protection.
Returns:
str: A base64 URL-safe encoded string that serves as the state.
"""
return (
base64.urlsafe_b64encode(secrets.token_bytes(16)).rstrip(b"=").decode("utf-8")
)
def main():
parser: argparse.ArgumentParser = argparse.ArgumentParser(
description="PKCE helper utilities: generate code verifier, code challenge, and state."
)
parser.add_argument(
"--generate",
choices=["verifier", "challenge", "state", "all"],
required=True,
help="What to generate: verifier, challenge, or state",
)
parser.add_argument(
"--verifier", help="The code verifier to use for generating a code challenge"
)
args: argparse.Namespace = parser.parse_args()
if args.generate == "verifier":
verifier = generate_code_verifier()
print(f"verifier={verifier}")
elif args.generate == "challenge":
if not args.verifier:
print("No verifier provided. Use --verifier to specify the code verifier.")
print("Generating a new verifier instead.")
args.verifier = generate_code_verifier()
challenge = generate_code_challenge(args.verifier)
print(f"verifier={args.verifier}")
print(f"challenge={challenge}")
elif args.generate == "state":
state = generate_state()
print(f"state={state}")
elif args.generate == "all":
verifier = generate_code_verifier()
challenge = generate_code_challenge(verifier)
state = generate_state()
print(f"verifier={verifier}")
print(f"challenge={challenge}")
print(f"state={state}")
if __name__ == "__main__":
main()