Coverage for polar/user/endpoints.py: 83%

36 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-12-05 17:15 +0000

1from fastapi import Depends 1a

2 

3from polar.auth.dependencies import Authenticator, WebUserRead, WebUserWrite 1a

4from polar.auth.models import AuthSubject 1a

5from polar.customer_portal.endpoints.downloadables import router as downloadables_router 1a

6from polar.customer_portal.endpoints.license_keys import router as license_keys_router 1a

7from polar.customer_portal.endpoints.order import router as order_router 1a

8from polar.customer_portal.endpoints.subscription import router as subscription_router 1a

9from polar.models import User 1a

10from polar.models.user import OAuthPlatform 1a

11from polar.openapi import APITag 1a

12from polar.postgres import AsyncSession, get_db_session 1a

13from polar.routing import APIRouter 1a

14from polar.user.oauth_service import oauth_account_service 1a

15from polar.user.service import user as user_service 1a

16 

17from .schemas import ( 1a

18 UserDeletionResponse, 

19 UserIdentityVerification, 

20 UserRead, 

21 UserScopes, 

22) 

23 

24router = APIRouter(prefix="/users", tags=["users", APITag.private]) 1a

25 

26# Include customer portal endpoints for backwards compatibility 

27router.include_router(order_router, deprecated=True, include_in_schema=False) 1a

28router.include_router(subscription_router, deprecated=True, include_in_schema=False) 1a

29router.include_router(downloadables_router, deprecated=True, include_in_schema=False) 1a

30router.include_router(license_keys_router, deprecated=True, include_in_schema=False) 1a

31 

32 

33@router.get("/me", response_model=UserRead) 1a

34async def get_authenticated(auth_subject: WebUserRead) -> User: 1a

35 return auth_subject.subject 

36 

37 

38@router.get("/me/scopes", response_model=UserScopes) 1a

39async def scopes( 1ab

40 auth_subject: AuthSubject[User] = Depends(Authenticator(allowed_subjects={User})), 

41) -> UserScopes: 

42 return UserScopes(scopes=list(auth_subject.scopes)) 

43 

44 

45@router.post("/me/identity-verification", response_model=UserIdentityVerification) 1a

46async def create_identity_verification( 1a

47 auth_subject: WebUserWrite, 

48 session: AsyncSession = Depends(get_db_session), 

49) -> UserIdentityVerification: 

50 return await user_service.create_identity_verification( 

51 session, user=auth_subject.subject 

52 ) 

53 

54 

55@router.delete( 1a

56 "/me", 

57 response_model=UserDeletionResponse, 

58 responses={ 

59 200: {"description": "Deletion result"}, 

60 }, 

61) 

62async def delete_authenticated_user( 1a

63 auth_subject: WebUserWrite, 

64 session: AsyncSession = Depends(get_db_session), 

65) -> UserDeletionResponse: 

66 """ 

67 Delete the authenticated user account. 

68 

69 A user can only be deleted if all organizations they are members of have been 

70 deleted first. If the user has active organizations, the response will include 

71 the list of organizations that must be deleted before the user account can be 

72 removed. 

73 

74 When deleted: 

75 - User's email is anonymized 

76 - User's avatar and metadata are cleared 

77 - User's OAuth accounts are deleted (cascade) 

78 - User's Account (payout account) is deleted if present 

79 """ 

80 return await user_service.request_deletion(session, auth_subject.subject) 

81 

82 

83@router.delete( 1a

84 "/me/oauth-accounts/{platform}", 

85 status_code=204, 

86 responses={ 

87 404: {"description": "OAuth account not found"}, 

88 400: {"description": "Cannot disconnect last authentication method"}, 

89 }, 

90) 

91async def disconnect_oauth_account( 1a

92 platform: OAuthPlatform, 

93 auth_subject: WebUserWrite, 

94 session: AsyncSession = Depends(get_db_session), 

95) -> None: 

96 """ 

97 Disconnect an OAuth account (GitHub or Google) from the authenticated user. 

98 

99 This allows users to unlink their OAuth provider while keeping their Polar account. 

100 They can still authenticate using other methods (email magic link or other OAuth providers). 

101 

102 Note: You cannot disconnect your last authentication method if your email is not verified. 

103 """ 

104 user = auth_subject.subject 

105 await oauth_account_service.disconnect_platform(session, user, platform)