mirror of https://github.com/tiangolo/fastapi.git
♻️ Update OAuth2 class utilities to be dependencies
This commit is contained in:
parent
8f16868c6a
commit
4a0922ebab
|
|
@ -1,24 +1,13 @@
|
||||||
from typing import List, Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi.openapi.models import OAuth2 as OAuth2Model, OAuthFlows as OAuthFlowsModel
|
from fastapi.openapi.models import OAuth2 as OAuth2Model, OAuthFlows as OAuthFlowsModel
|
||||||
from fastapi.params import Form
|
from fastapi.params import Form
|
||||||
from fastapi.security.base import SecurityBase
|
from fastapi.security.base import SecurityBase
|
||||||
from pydantic import BaseModel, Schema
|
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.status import HTTP_403_FORBIDDEN
|
from starlette.status import HTTP_403_FORBIDDEN
|
||||||
|
|
||||||
|
|
||||||
class OAuth2PasswordRequestData(BaseModel):
|
|
||||||
grant_type: str = "password"
|
|
||||||
username: str
|
|
||||||
password: str
|
|
||||||
scope: Optional[List[str]] = None
|
|
||||||
# Client ID and secret might come from headers
|
|
||||||
client_id: Optional[str] = None
|
|
||||||
client_secret: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class OAuth2PasswordRequestForm:
|
class OAuth2PasswordRequestForm:
|
||||||
"""
|
"""
|
||||||
This is a dependency class, use it like:
|
This is a dependency class, use it like:
|
||||||
|
|
@ -28,7 +17,7 @@ class OAuth2PasswordRequestForm:
|
||||||
data = form_data.parse()
|
data = form_data.parse()
|
||||||
print(data.username)
|
print(data.username)
|
||||||
print(data.password)
|
print(data.password)
|
||||||
for scope in data.scope:
|
for scope in data.scopes:
|
||||||
print(scope)
|
print(scope)
|
||||||
if data.client_id:
|
if data.client_id:
|
||||||
print(data.client_id)
|
print(data.client_id)
|
||||||
|
|
@ -40,8 +29,8 @@ class OAuth2PasswordRequestForm:
|
||||||
It creates the following Form request parameters in your endpoint:
|
It creates the following Form request parameters in your endpoint:
|
||||||
|
|
||||||
grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password".
|
grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password".
|
||||||
Nevertheless, this model is permissive and allows not passing it. If you want to enforce it,
|
Nevertheless, this dependency class is permissive and allows not passing it. If you want to enforce it,
|
||||||
use instead the OAuth2PasswordRequestFormStrict model.
|
use instead the OAuth2PasswordRequestFormStrict dependency.
|
||||||
username: username string. The OAuth2 spec requires the exact field name "username".
|
username: username string. The OAuth2 spec requires the exact field name "username".
|
||||||
password: password string. The OAuth2 spec requires the exact field name "password".
|
password: password string. The OAuth2 spec requires the exact field name "password".
|
||||||
scope: Optional string. Several scopes (each one a string) separated by spaces. E.g.
|
scope: Optional string. Several scopes (each one a string) separated by spaces. E.g.
|
||||||
|
|
@ -50,9 +39,6 @@ class OAuth2PasswordRequestForm:
|
||||||
using HTTP Basic auth, as: client_id:client_secret
|
using HTTP Basic auth, as: client_id:client_secret
|
||||||
client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
|
client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
|
||||||
using HTTP Basic auth, as: client_id:client_secret
|
using HTTP Basic auth, as: client_id:client_secret
|
||||||
|
|
||||||
|
|
||||||
It has the method parse() that returns a model with all the same data and the scopes extracted as a list of strings.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -67,24 +53,61 @@ class OAuth2PasswordRequestForm:
|
||||||
self.grant_type = grant_type
|
self.grant_type = grant_type
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.scope = scope
|
self.scopes = scope.split()
|
||||||
self.client_id = client_id
|
self.client_id = client_id
|
||||||
self.client_secret = client_secret
|
self.client_secret = client_secret
|
||||||
|
|
||||||
def parse(self) -> OAuth2PasswordRequestData:
|
|
||||||
return OAuth2PasswordRequestData(
|
|
||||||
grant_type=self.grant_type,
|
|
||||||
username=self.username,
|
|
||||||
password=self.password,
|
|
||||||
scope=self.scope.split(),
|
|
||||||
client_id=self.client_id,
|
|
||||||
client_secret=self.client_secret,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
|
||||||
# The OAuth2 spec says it MUST have the value "password"
|
"""
|
||||||
grant_type: str = Schema(..., regex="password")
|
This is a dependency class, use it like:
|
||||||
|
|
||||||
|
@app.post("/login")
|
||||||
|
def login(form_data: Oauth2PasswordRequestFormStrict = Depends()):
|
||||||
|
data = form_data.parse()
|
||||||
|
print(data.username)
|
||||||
|
print(data.password)
|
||||||
|
for scope in data.scopes:
|
||||||
|
print(scope)
|
||||||
|
if data.client_id:
|
||||||
|
print(data.client_id)
|
||||||
|
if data.client_secret:
|
||||||
|
print(data.client_secret)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
It creates the following Form request parameters in your endpoint:
|
||||||
|
|
||||||
|
grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password".
|
||||||
|
This dependency is strict about it. If you want to be permissive, use instead the
|
||||||
|
OAuth2PasswordRequestFormStrict dependency class.
|
||||||
|
username: username string. The OAuth2 spec requires the exact field name "username".
|
||||||
|
password: password string. The OAuth2 spec requires the exact field name "password".
|
||||||
|
scope: Optional string. Several scopes (each one a string) separated by spaces. E.g.
|
||||||
|
"items:read items:write users:read profile openid"
|
||||||
|
client_id: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
|
||||||
|
using HTTP Basic auth, as: client_id:client_secret
|
||||||
|
client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any)
|
||||||
|
using HTTP Basic auth, as: client_id:client_secret
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
grant_type: str = Form(..., regex="password"),
|
||||||
|
username: str = Form(...),
|
||||||
|
password: str = Form(...),
|
||||||
|
scope: str = Form(""),
|
||||||
|
client_id: Optional[str] = Form(None),
|
||||||
|
client_secret: Optional[str] = Form(None),
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
grant_type=grant_type,
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
scope=scope,
|
||||||
|
client_id=client_id,
|
||||||
|
client_secret=client_secret,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OAuth2(SecurityBase):
|
class OAuth2(SecurityBase):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue