Skip to main content

Extended FastAPI OAuth2PasswordRequestForm

So, to register a user, I needed some additional fields that were not in the standard form:

@router.post("/sign-up/", response_model=TokensScheme)
async def sign_up(form_data: OAuth2PasswordRequestForm = Depends(),
                  email: str = Body(),
                  first_name: str | None = Body(default=None),
                  second_name: str | None = Body(default=None),
                  session: Session = Depends(get_db)):
    repository = UsersRepository(session)

    repository.create(
        User(
            email=email,
            username=form_data.username,
            password=get_password_hash(form_data.password),
            first_name=first_name,
            second_name=second_name,
            is_active=False,
        )
    )

    return sign_in(form_data, session)

This route successfully fulfills its task, however, I did not really like adding fields in this way and I decided to extend the standard form as follows:

class UserBaseScheme(BaseModel):
    email: str
    username: str
    first_name: str | None
    second_name: str | None


class UserCreateScheme(UserBaseScheme):
    password: str


class OAuth2ExtendedForm(OAuth2PasswordRequestForm,
                         UserCreateScheme):
    pass


@router.post("/sign-up/", response_model=TokensScheme)
async def sign_up(form_data: OAuth2ExtendedForm = Depends(),
                  session: Session = Depends(get_db)):
    repository = UsersRepository(session)

    repository.create(
        User(
            email=form_data.email,
            username=form_data.username,
            password=get_password_hash(form_data.password),
            first_name=form_data.first_name,
            second_name=form_data.second_name,
            is_active=False,
        )
    )

    return sign_in(form_data, session)

I don't know if it's a good idea to mix Pydantic model and form, but it doesn't work:

ValueError: "OAuth2ExtendedForm" object has no field "grant_type"

I tried to tried to remove inheritance from UserCreateScheme and enter the fields I need directly into the new form, as in this question:

class OAuth2ExtendedForm(OAuth2PasswordRequestForm):
    email: str
    first_name: str | None
    second_name: str | None

It works, but it absolutely ignores the new attributes, they just don't exist in the form.

The question is: is this some mistake of mine, FastAPI's internal security? Is it possible to expand this form at all? Thanks in advance.

EDIT:

I tried different combinations, maybe I really made a mistake somewhere.

So, as Daniil Fajnberg said, it would be nice to provide some example:

from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordRequestForm
from pydantic import BaseModel

app = FastAPI()


class UserBaseScheme(BaseModel):
    email: str
    username: str
    first_name: str | None
    second_name: str | None


class UserCreateScheme(UserBaseScheme):
    password: str


class OAuth2ExtendedFormManual(OAuth2PasswordRequestForm):
    email: str


class OAuth2ExtendedFormGrantType(OAuth2PasswordRequestForm):
    email: str
    grant_type: str


class OAuth2ExtendedFormInherit(OAuth2PasswordRequestForm,
                                UserCreateScheme):
    pass


"""
    Trying to send this as a form-data to all routes: 
    1. {'username': 'boo', 'password': 'boo', 'email': 'boo@gmail.com}
    2. {'username': 'boo', 'password': 'boo', 'email': 'boo@gmail.com, 'grant_type': 'password'}
    
    In both cases I can't get the expected result.
"""


@app.post("/sign-up-standard/")
async def sign_up(form_data: OAuth2PasswordRequestForm = Depends()):
    print(form_data.__dict__)
    # 1, 2 -> {
    #   'grant_type': None | 'password',
    #   'username': 'boo',
    #   'password': 'boo',
    #   'scopes': [],
    #   'client_id': None,
    #   'client_secret': None
    # }
    # Where is an email field?


@app.post("/sign-up-extended-manual/")
async def sign_up(form_data: OAuth2ExtendedFormManual = Depends()):
    print(form_data.__dict__)
    # 1, 2 -> {
    #   'grant_type': None | 'password',
    #   'username': 'boo',
    #   'password': 'boo',
    #   'scopes': [],
    #   'client_id': None,
    #   'client_secret': None
    # }
    # Where is an email field?


@app.post("/sign-up-extended-manual-grant-type/")
async def sign_up(form_data: OAuth2ExtendedFormGrantType = Depends()):
    print(form_data.__dict__)
    # 1, 2 -> {
    #   'grant_type': None | 'password',
    #   'username': 'boo',
    #   'password': 'boo',
    #   'scopes': [],
    #   'client_id': None,
    #   'client_secret': None
    # }
    # Where is an email field?


@app.post("/sign-up-extended-inherit/")
async def sign_up(form_data: OAuth2ExtendedFormInherit = Depends()):
    print(form_data.__dict__)
    # 1, 2 -> File "pydantic\main.py", line 358, in pydantic.main.BaseModel.__setattr__
    # ValueError: "OAuth2ExtendedFormInherit" object has no field "grant_type"

EDIT 2:

Ok, it works like a standard:

class OAuth2ExtendedForm(OAuth2PasswordRequestForm):
    pass

This doesn't work:

class OAuth2ExtendedForm(OAuth2PasswordRequestForm):
    def __init__(self,
                 email: str = Form(),
                 first_name: str | None = Form(None),
                 second_name: str | None = Form(None)):
        super().__init__()
        self.email = email
        self.first_name = first_name
        self.second_name = second_name
# It does not process fields
self.scopes = scope.split() # in constructor
AttributeError: 'Form' object has no attribute 'split'

It's terrible, but it works exactly as I need it:

class OAuth2ExtendedForm(OAuth2PasswordRequestForm):
    def __init__(self,
                 email: str = Form(),
                 first_name: str | None = Form(None),
                 second_name: str | None = Form(None),
                 grant_type: str = Form(default=None, regex="password"),
                 username: str = Form(),
                 password: str = Form(),
                 scope: str = Form(default=""),
                 client_id: Optional[str] = Form(default=None),
                 client_secret: Optional[str] = Form(default=None)
                 ):
        super().__init__(grant_type=grant_type, 
                         username=username, 
                         password=password,
                         scope=scope, 
                         client_id=client_id, 
                         client_secret=client_secret)
        
        self.email = email
        self.first_name = first_name
        self.second_name = second_name

I also try use kwargs, but Pydantic said this:

{
    "detail": [
        {
            "loc": [
                "query",
                "kwargs"
            ],
            "msg": "field required",
            "type": "value_error.missing"
        }
    ]
}


source https://stackoverflow.com/questions/75188072/extended-fastapi-oauth2passwordrequestform

Comments

Popular posts from this blog

Prop `className` did not match in next js app

I have written a sample code ( Github Link here ). this is a simple next js app, but giving me error when I refresh the page. This seems to be the common problem and I tried the fix provided in the internet but does not seem to fix my issue. The error is Warning: Prop className did not match. Server: "MuiBox-root MuiBox-root-1" Client: "MuiBox-root MuiBox-root-2". Did changes for _document.js, modified _app.js as mentioned in official website and solutions in stackoverflow. but nothing seems to work. Could someone take a look and help me whats wrong with the code? Via Active questions tagged javascript - Stack Overflow https://ift.tt/2FdjaAW

How to show number of registered users in Laravel based on usertype?

i'm trying to display data from the database in the admin dashboard i used this: <?php use Illuminate\Support\Facades\DB; $users = DB::table('users')->count(); echo $users; ?> and i have successfully get the correct data from the database but what if i want to display a specific data for example in this user table there is "usertype" that specify if the user is normal user or admin i want to user the same code above but to display a specific usertype i tried this: <?php use Illuminate\Support\Facades\DB; $users = DB::table('users')->count()->WHERE usertype =admin; echo $users; ?> but it didn't work, what am i doing wrong? source https://stackoverflow.com/questions/68199726/how-to-show-number-of-registered-users-in-laravel-based-on-usertype

Why is my reports service not connecting?

I am trying to pull some data from a Postgres database using Node.js and node-postures but I can't figure out why my service isn't connecting. my routes/index.js file: const express = require('express'); const router = express.Router(); const ordersCountController = require('../controllers/ordersCountController'); const ordersController = require('../controllers/ordersController'); const weeklyReportsController = require('../controllers/weeklyReportsController'); router.get('/orders_count', ordersCountController); router.get('/orders', ordersController); router.get('/weekly_reports', weeklyReportsController); module.exports = router; My controllers/weeklyReportsController.js file: const weeklyReportsService = require('../services/weeklyReportsService'); const weeklyReportsController = async (req, res) => { try { const data = await weeklyReportsService; res.json({data}) console