99from fastapi import APIRouter , Depends , Request
1010from fastui import AnyComponent , FastUI
1111from fastui import components as c
12- from fastui .auth import GitHubAuthProvider
12+ from fastui .auth import AuthRedirect , GitHubAuthProvider
1313from fastui .events import AuthEvent , GoToEvent , PageEvent
1414from fastui .forms import fastui_form
1515from httpx import AsyncClient
2020
2121router = APIRouter ()
2222
23-
23+ GITHUB_CLIENT_ID = os . getenv ( 'GITHUB_CLIENT_ID' , '0d0315f9c2e055d032e2' )
2424# this will give an error when making requests to GitHub, but at least the app will run
2525GITHUB_CLIENT_SECRET = SecretStr (os .getenv ('GITHUB_CLIENT_SECRET' , 'dummy-secret' ))
26+ # use 'http://localhost:3000/auth/login/github/redirect' in development
27+ GITHUB_REDIRECT = os .getenv ('GITHUB_REDIRECT' )
2628
2729
2830async def get_github_auth (request : Request ) -> GitHubAuthProvider :
2931 client : AsyncClient = request .app .state .httpx_client
3032 return GitHubAuthProvider (
3133 httpx_client = client ,
32- github_client_id = '9eddf87b27f71f52194a' ,
34+ github_client_id = GITHUB_CLIENT_ID ,
3335 github_client_secret = GITHUB_CLIENT_SECRET ,
36+ redirect_uri = GITHUB_REDIRECT ,
3437 scopes = ['user:email' ],
3538 )
3639
@@ -39,44 +42,42 @@ async def get_github_auth(request: Request) -> GitHubAuthProvider:
3942
4043
4144@router .get ('/login/{kind}' , response_model = FastUI , response_model_exclude_none = True )
42- async def auth_login (
45+ def auth_login (
4346 kind : LoginKind ,
44- user : Annotated [User | None , Depends (User .from_request )],
45- github_auth : Annotated [GitHubAuthProvider , Depends (get_github_auth )],
47+ user : Annotated [User | None , Depends (User .from_request_opt )],
4648) -> list [AnyComponent ]:
47- if user is None :
48- return demo_page (
49- c .LinkList (
50- links = [
51- c .Link (
52- components = [c .Text (text = 'Password Login' )],
53- on_click = PageEvent (name = 'tab' , push_path = '/auth/login/password' , context = {'kind' : 'password' }),
54- active = '/auth/login/password' ,
55- ),
56- c .Link (
57- components = [c .Text (text = 'GitHub Login' )],
58- on_click = PageEvent (name = 'tab' , push_path = '/auth/login/github' , context = {'kind' : 'github' }),
59- active = '/auth/login/github' ,
60- ),
61- ],
62- mode = 'tabs' ,
63- class_name = '+ mb-4' ,
64- ),
65- c .ServerLoad (
66- path = '/auth/login/content/{kind}' ,
67- load_trigger = PageEvent (name = 'tab' ),
68- components = await auth_login_content (kind , github_auth ),
69- ),
70- title = 'Authentication' ,
71- )
72- else :
73- return [c .FireEvent (event = GoToEvent (url = '/auth/profile' ))]
49+ if user is not None :
50+ # already logged in
51+ raise AuthRedirect ('/auth/profile' )
52+
53+ return demo_page (
54+ c .LinkList (
55+ links = [
56+ c .Link (
57+ components = [c .Text (text = 'Password Login' )],
58+ on_click = PageEvent (name = 'tab' , push_path = '/auth/login/password' , context = {'kind' : 'password' }),
59+ active = '/auth/login/password' ,
60+ ),
61+ c .Link (
62+ components = [c .Text (text = 'GitHub Login' )],
63+ on_click = PageEvent (name = 'tab' , push_path = '/auth/login/github' , context = {'kind' : 'github' }),
64+ active = '/auth/login/github' ,
65+ ),
66+ ],
67+ mode = 'tabs' ,
68+ class_name = '+ mb-4' ,
69+ ),
70+ c .ServerLoad (
71+ path = '/auth/login/content/{kind}' ,
72+ load_trigger = PageEvent (name = 'tab' ),
73+ components = auth_login_content (kind ),
74+ ),
75+ title = 'Authentication' ,
76+ )
7477
7578
7679@router .get ('/login/content/{kind}' , response_model = FastUI , response_model_exclude_none = True )
77- async def auth_login_content (
78- kind : LoginKind , github_auth : Annotated [GitHubAuthProvider , Depends (get_github_auth )]
79- ) -> list [AnyComponent ]:
80+ def auth_login_content (kind : LoginKind ) -> list [AnyComponent ]:
8081 match kind :
8182 case 'password' :
8283 return [
@@ -87,16 +88,15 @@ async def auth_login_content(
8788 'here you can "login" with any email address and password.'
8889 )
8990 ),
90- c .Paragraph (text = '(Passwords are not saved and email stored in the browser via a JWT)' ),
91+ c .Paragraph (text = '(Passwords are not saved and is email stored in the browser via a JWT only )' ),
9192 c .ModelForm (model = LoginForm , submit_url = '/api/auth/login' ),
9293 ]
9394 case 'github' :
94- auth_url = await github_auth .authorization_url ()
9595 return [
9696 c .Heading (text = 'GitHub Login' , level = 3 ),
9797 c .Paragraph (text = 'Demo of GitHub authentication.' ),
98- c .Paragraph (text = '(Credentials are stored in the browser via a JWT)' ),
99- c .Button (text = 'Login with GitHub' , on_click = GoToEvent (url = auth_url )),
98+ c .Paragraph (text = '(Credentials are stored in the browser via a JWT only )' ),
99+ c .Button (text = 'Login with GitHub' , on_click = GoToEvent (url = '/auth/login/github/gen' )),
100100 ]
101101 case _:
102102 raise ValueError (f'Invalid kind { kind !r} ' )
@@ -121,30 +121,33 @@ async def login_form_post(form: Annotated[LoginForm, fastui_form(LoginForm)]) ->
121121
122122
123123@router .get ('/profile' , response_model = FastUI , response_model_exclude_none = True )
124- async def profile (user : Annotated [User | None , Depends (User .from_request )]) -> list [AnyComponent ]:
125- if user is None :
126- return [c .FireEvent (event = GoToEvent (url = '/auth/login' ))]
127- else :
128- return demo_page (
129- c .Paragraph (text = f'You are logged in as "{ user .email } ".' ),
130- c .Button (text = 'Logout' , on_click = PageEvent (name = 'submit-form' )),
131- c .Heading (text = 'User Data:' , level = 3 ),
132- c .Code (language = 'json' , text = json .dumps (asdict (user ), indent = 2 )),
133- c .Form (
134- submit_url = '/api/auth/logout' ,
135- form_fields = [c .FormFieldInput (name = 'test' , title = '' , initial = 'data' , html_type = 'hidden' )],
136- footer = [],
137- submit_trigger = PageEvent (name = 'submit-form' ),
138- ),
139- title = 'Authentication' ,
140- )
124+ async def profile (user : Annotated [User , Depends (User .from_request )]) -> list [AnyComponent ]:
125+ return demo_page (
126+ c .Paragraph (text = f'You are logged in as "{ user .email } ".' ),
127+ c .Button (text = 'Logout' , on_click = PageEvent (name = 'submit-form' )),
128+ c .Heading (text = 'User Data:' , level = 3 ),
129+ c .Code (language = 'json' , text = json .dumps (asdict (user ), indent = 2 )),
130+ c .Form (
131+ submit_url = '/api/auth/logout' ,
132+ form_fields = [c .FormFieldInput (name = 'test' , title = '' , initial = 'data' , html_type = 'hidden' )],
133+ footer = [],
134+ submit_trigger = PageEvent (name = 'submit-form' ),
135+ ),
136+ title = 'Authentication' ,
137+ )
141138
142139
143140@router .post ('/logout' , response_model = FastUI , response_model_exclude_none = True )
144141async def logout_form_post () -> list [AnyComponent ]:
145142 return [c .FireEvent (event = AuthEvent (token = False , url = '/auth/login/password' ))]
146143
147144
145+ @router .get ('/login/github/gen' , response_model = FastUI , response_model_exclude_none = True )
146+ async def auth_github_gen (github_auth : Annotated [GitHubAuthProvider , Depends (get_github_auth )]) -> list [AnyComponent ]:
147+ auth_url = await github_auth .authorization_url ()
148+ return [c .FireEvent (event = GoToEvent (url = auth_url ))]
149+
150+
148151@router .get ('/login/github/redirect' , response_model = FastUI , response_model_exclude_none = True )
149152async def github_redirect (
150153 code : str ,
0 commit comments