Skip to content

Commit

Permalink
Prep the tests: Create user_code_generator util
Browse files Browse the repository at this point in the history
This creates a user friendly but still high entropy user code to be used
in the device flow
  • Loading branch information
duzumaki committed Jan 14, 2025
1 parent 2fb2929 commit 968dbea
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 0 deletions.
43 changes: 43 additions & 0 deletions oauth2_provider/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import functools
import random

from django.conf import settings
from jwcrypto import jwk
Expand Down Expand Up @@ -32,3 +33,45 @@ def get_timezone(time_zone):

return pytz.timezone(time_zone)
return zoneinfo.ZoneInfo(time_zone)


def user_code_generator(user_code_length: int = 8) -> str:
"""
Recommended user code that retains enough entropy but doesn't
ruin the user experience of typing the code in.
the below is based off:
https://datatracker.ietf.org/doc/html/rfc8628#section-5.1
but with added explanation as to where 34.5 bits of entropy is coming from
entropy (in bits) = length of user code * log2(length of set of chars)
e = 8 * log2(20)
e = 34.5
log2(20) is used here to say "you can make 20 yes/no decisions per user code single input character".
_ _ _ _ - _ _ _ _ = 20^8 ~= 2^35.5
*
* you have 20 choices of chars to choose from (20 yes no decisions)
and so on for the other 7 spaces
in english this means an attacker would need to try
2^34.5 unique combinations to exhaust all possibilities.
however with a user code only being valid for 30 seconds
and rate limiting, a brute force attack is extremely unlikely
to work
for our function we'll be using a base 32 character set
"""

# base32 character space
character_space = "0123456789ABCDEFGHIJKLMNOPQRSTUV"

# being explicit with length
user_code = [""] * user_code_length

for i in range(user_code_length):
user_code[i] = random.choice(character_space)

return "".join(user_code)
4 changes: 4 additions & 0 deletions tests/app/idp/idp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import environ

from oauth2_provider.utils import user_code_generator


# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Expand Down Expand Up @@ -199,6 +201,8 @@

OAUTH2_PROVIDER = {
"OAUTH2_VALIDATOR_CLASS": "idp.oauth.CustomOAuth2Validator",
"OAUTH_DEVICE_VERIFICATION_URI": "http://127.0.0.1:8000/o/device",
"OAUTH_DEVICE_USER_CODE_GENERATOR": lambda: user_code_generator(),
"OIDC_ENABLED": env("OAUTH2_PROVIDER_OIDC_ENABLED"),
"OIDC_RP_INITIATED_LOGOUT_ENABLED": env("OAUTH2_PROVIDER_OIDC_RP_INITIATED_LOGOUT_ENABLED"),
# this key is just for out test app, you should never store a key like this in a production environment.
Expand Down

0 comments on commit 968dbea

Please sign in to comment.