Skip to content

Commit 2b7603d

Browse files
committed
Basic support for connected accounts/mrrt
1 parent e84e8d2 commit 2b7603d

File tree

3 files changed

+65
-2
lines changed

3 files changed

+65
-2
lines changed

src/auth0_fastapi/auth/auth_client.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
# Imported from auth0-server-python
33
from auth0_server_python.auth_server.server_client import ServerClient
4-
from auth0_server_python.auth_types import LogoutOptions, StartInteractiveLoginOptions
4+
from auth0_server_python.auth_types import LogoutOptions, StartInteractiveLoginOptions, ConnectAccountOptions
55
from fastapi import HTTPException, Request, Response, status
66

77
from auth0_fastapi.config import Auth0Config
@@ -45,6 +45,7 @@ def __init__(
4545
transaction_store=transaction_store,
4646
state_store=state_store,
4747
pushed_authorization_requests=config.pushed_authorization_requests,
48+
use_mrrt=config.use_mrrt,
4849
authorization_params={
4950
"audience": config.audience,
5051
"redirect_uri": redirect_uri,
@@ -82,6 +83,36 @@ async def complete_login(
8283
"""
8384
return await self.client.complete_interactive_login(callback_url, store_options=store_options)
8485

86+
async def start_connect_account(
87+
self,
88+
connection: str,
89+
authorization_params: dict = None,
90+
store_options: dict = None,
91+
) -> str:
92+
"""
93+
Initiates the connected account process.
94+
Optionally, an app_state dictionary can be passed to persist additional state.
95+
Returns the authorization URL to redirect the user.
96+
"""
97+
options = ConnectAccountOptions(
98+
connection=connection,
99+
authorization_params= authorization_params
100+
)
101+
return await self.client.start_connect_account(options=options, store_options=store_options)
102+
103+
async def complete_connect_account(
104+
self,
105+
connect_code: str,
106+
state: str,
107+
store_options: dict = None,
108+
) -> str:
109+
"""
110+
Initiates the interactive login process.
111+
Optionally, an app_state dictionary can be passed to persist additional state.
112+
Returns the authorization URL to redirect the user.
113+
"""
114+
return await self.client.complete_connect_account(connect_code=connect_code, state=state, store_options=store_options)
115+
85116
async def logout(
86117
self,
87118
return_to: str = None,

src/auth0_fastapi/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ class Auth0Config(BaseModel):
1515
audience: Optional[str] = Field(None, description="Target audience for tokens (if applicable)")
1616
authorization_params: Optional[dict[str, Any]] = Field(None, description="Additional parameters to include in the authorization request")
1717
pushed_authorization_requests: bool = Field(False, description="Whether to use pushed authorization requests")
18+
use_mrrt: bool = Field(False, description="Whether to use Multi-Resource Refresh Tokens (MRRT)")
1819
# Route-mounting flags with desired defaults
1920
mount_routes: bool = Field(True, description="Controls /auth/* routes: login, logout, callback, backchannel-logout")
2021
mount_connect_routes: bool = Field(False, description="Controls /auth/connect routes (account-linking)")
22+
mount_connected_account_routes: bool = Field(False, description="Controls /auth/connect-account routes (for connected accounts)")
2123
#Cookie Settings
2224
cookie_name: str = Field("_a0_session", description="Name of the cookie storing session data")
2325
session_expiration: int = Field(259200, description="Session expiration time in seconds (default: 3 days)")

src/auth0_fastapi/server/routes.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,20 @@ async def callback(
5858
):
5959
"""
6060
Endpoint to handle the callback after Auth0 authentication.
61-
Processes the callback URL and completes the login flow.
61+
Processes the callback URL and completes the login or connected account flow.
6262
Redirects the user to a post-login URL based on appState or a default.
6363
"""
64+
connect_code = request.query_params.get("connect_code")
65+
if connect_code and config.mount_connected_account_routes:
66+
state = request.query_params.get("state")
67+
return await auth_client.complete_connect_account(
68+
connect_code=connect_code,
69+
state=state,
70+
store_options={"request": request, "response": response},
71+
)
72+
6473
full_callback_url = str(request.url)
74+
6575
try:
6676
session_data = await auth_client.complete_login(
6777
full_callback_url,
@@ -123,7 +133,27 @@ async def backchannel_logout(
123133
raise HTTPException(status_code=400, detail=str(e))
124134
return Response(status_code=204)
125135

136+
if config.mount_connected_account_routes:
137+
@router.get("/auth/connect-account")
138+
async def connect_account(
139+
request: Request,
140+
response: Response,
141+
connection: str = Query(),
142+
auth_client: AuthClient = Depends(get_auth_client),
143+
):
144+
"""
145+
Endpoint to initiate the connect account flow for linking a third-party account to the user's profile.
146+
Redirects the user to the Auth0 connect account URL.
147+
"""
148+
authorization_params = {k: v for k, v in request.query_params.items() if k not in [
149+
"connection"]}
150+
connect_account_url = await auth_client.start_connect_account(
151+
connection=connection,
152+
authorization_params=authorization_params,
153+
store_options={"request": request, "response": response},
154+
)
126155

156+
return RedirectResponse(url=connect_account_url, headers=response.headers)
127157

128158
if config.mount_connect_routes:
129159

0 commit comments

Comments
 (0)