Open mjvdvlugt opened 3 years ago
An alternative solution could also parse the "sub" uuid and scopes('cognito:groups') from the Access Token Payload, and make username/email optional. I found some claims contents documentation: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html#amazon-cognito-user-pools-using-the-access-token
By the way: I've managed to work around this using a custom CognitoClaim object. 💪
I thought of another possibility that might help:
class CognitoClaims(BaseModel):
username: str = Field(alias="cognito:username")
email: str = Field(None, alias="email")
class Config:
extra = Extra.allow
The extra = Extra.allow
will make Pydantic accept all fields from the auth provider. With this also any added custom fields are automatically accessible through the user_info
object.
A combination of this with allow_population_by_field_name = True
is also possible of course.
@mjvdvlugt Always thank you for your feedback and proposals. Very helpful !!!
I would like to investigate a little further whether to make it the default configuration.
I thought of another possibility that might help:
class CognitoClaims(BaseModel): username: str = Field(alias="cognito:username") email: str = Field(None, alias="email") class Config: extra = Extra.allow
The
extra = Extra.allow
will make Pydantic accept all fields from the auth provider. With this also any added custom fields are automatically accessible through theuser_info
object.A combination of this with
allow_population_by_field_name = True
is also possible of course.
A small note - if you want to avoid modifying the package code you'll also need to create a custom CognitoCurrentUser
class, something like:
class CognitoClaimsCustom(BaseModel):
username: str = Field(alias="cognito:username")
email: str = Field(None, alias="email")
sub: str = Field(None, alias="cognito:sub")
class Config:
extra = Extra.allow
class CognitoCurrentUserCustom(CognitoCurrentUser):
user_info = CognitoClaimsCustom
def __init__(
self, *args: Any, **kwargs: Any,
):
super().__init__(*args, **kwargs)
Otherwise the CognitoCurrentUser instance will by default use the original version of CognitoClaims which only provide the username and string. With this approach instead you get all claims.
When using
CognitoCurrentUser(region=settings.aws_region, userPoolId=settings.aws_cognito_user_pool_id)
in my endpoint depencies, it consistently errors a 403: "Validation Error for Claims". Further investigation indicated this has to do with an issue mapping the Cognito reply to the CognitoClaims Pydantic model here: https://github.com/tokusumi/fastapi-cloudauth/blob/a8db880b841149dc5cf4b76762a98d74daff21a2/fastapi_cloudauth/base.py#L232The ValidationError is:
and indeed, the 'cognito:username' field is not available in the claims:
but 'username' is!
I wasn't able to find why Cognito returns the claims in this different way.
Proposed compatibility fix
Adding the
allow_population_by_field_name = True
in the CognitoClaims model config makes it compatible with this other Cognito output.