pydantic-extra-types
pydantic-extra-types copied to clipboard
chore(feat): Support OTP as a type
import random
from pydantic import BaseModel, Field
from pydantic_extra_types import OTP, OTP_ALPHABET
class GenerateOTP(BaseModel):
"""
Generate an OTP
"""
length: int = Field(6, ge=6, le=6)
alphabet: str = Field(OTP_ALPHABET, min_length=6, max_length=32)
def generate(self) -> OTP:
return OTP(''.join(random.choices(self.alphabet, k=self.length)))
I'm a bit unclear on the usage here.
I think the api should perhaps be more like
class LoginForm(BaseModel):
password: str # whatever
ot_token: OTPToken
...
LoginForm.model_validate(form_data, context={'otp_secret': otp_secret_from_database_etc})
OTPToken then just raises a ValidationError if the token is incorrect, otherwise the value could just be None.
Does that make sense?
I'm a bit unclear on the usage here.
I think the api should perhaps be more like
class LoginForm(BaseModel): password: str # whatever ot_token: OTPToken ... LoginForm.model_validate(form_data, context={'otp_secret': otp_secret_from_database_etc})
OTPTokenthen just raises aValidationErrorif the token is incorrect, otherwise, the value could just beNone.Does that make sense?
I think this approach is clear, more than using OTP to validate!
I will try to make this approach!
Hey @samuelcolvin
I think we have schema close to this:
from pydantic import BaseModel
from pydantic_extra_types import OTPToken
import pyotp
class LoginForm(BaseModel):
username: str
password: str
ot_token: OTPToken
# Generate a secret key to be used for OTP generation
otp_secret = pyotp.random_base32()
# User input in the form of a dictionary
form_data = {
'username': 'user123',
'password': 'secret_password',
'ot_token': pyotp.TOTP(otp_secret).now()
}
# Validate the form data
form = LoginForm.model_validate(form_data, context={'otp_secret': otp_secret})
# Print the validated form data
print(f'Login successful for user {form}')
@samuelcolvin all the changes fixed, This Pull request is ready to merge