hyperpocket 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,7 +53,7 @@ class RedditOAuth2AuthHandler(AuthHandlerInterface):
53
53
  config().public_base_url + "/",
54
54
  f"{config().callback_url_rewrite_prefix}/auth/reddit/oauth2/callback",
55
55
  )
56
- print(f"redirect_uri: {redirect_uri}")
56
+
57
57
  auth_url = self._make_auth_url(
58
58
  req=auth_req, redirect_uri=redirect_uri, state=future_uid
59
59
  )
@@ -59,7 +59,7 @@ class SlackOAuth2AuthHandler(AuthHandlerInterface):
59
59
  config().public_base_url + "/",
60
60
  f"{config().callback_url_rewrite_prefix}/auth/slack/oauth2/callback",
61
61
  )
62
- print(f"redirect_uri: {redirect_uri}")
62
+
63
63
  auth_url = self._make_auth_url(
64
64
  req=auth_req, redirect_uri=redirect_uri, state=future_uid
65
65
  )
@@ -1,10 +1,11 @@
1
1
  import click
2
2
 
3
- from hyperpocket.cli.auth import create_token_auth_template
4
3
  from hyperpocket.cli.eject import eject
5
4
  from hyperpocket.cli.pull import pull
6
5
  from hyperpocket.cli.sync import sync
7
-
6
+ from hyperpocket.cli.eject import eject
7
+ from hyperpocket.cli.auth_token import create_token_auth_template
8
+ from hyperpocket.cli.auth_oauth2 import create_oauth2_auth_template
8
9
 
9
10
  @click.group()
10
11
  def cli():
@@ -18,6 +19,7 @@ def devtool():
18
19
 
19
20
  cli.add_command(devtool)
20
21
  devtool.add_command(create_token_auth_template)
22
+ devtool.add_command(create_oauth2_auth_template)
21
23
 
22
24
  cli.add_command(pull)
23
25
  cli.add_command(sync)
@@ -0,0 +1,90 @@
1
+ import os
2
+ import click
3
+ from pathlib import Path
4
+ from hyperpocket.cli.codegen.auth import get_auth_context_template, get_auth_oauth2_context_template, get_auth_oauth2_handler_template, get_auth_oauth2_schema_template, get_server_auth_oauth2_template
5
+
6
+ @click.command()
7
+ @click.argument('service_name', type=str)
8
+ @click.option('--force', is_flag=True, default=False)
9
+ def create_oauth2_auth_template(service_name, force):
10
+ ## Validate service_name
11
+ if not service_name.islower() or not service_name.replace('_', '').isalpha():
12
+ raise ValueError("service_name must be lowercase and contain only letters and underscores")
13
+
14
+ capitliazed_service_name = service_name.capitalize()
15
+ if '_' in service_name:
16
+ capitliazed_service_name = ''.join([word.capitalize() for word in service_name.split('_')])
17
+
18
+ ## Get the current working directory
19
+ cwd = Path.cwd()
20
+ parent_path = cwd.parent
21
+
22
+ generate_server_auth(service_name, parent_path, force)
23
+ generate_hyperpocket_auth_dir(service_name, parent_path, force)
24
+ generate_auth_context(service_name, capitliazed_service_name, parent_path, force)
25
+ generate_auth_oauth2_context(service_name, capitliazed_service_name, parent_path, force)
26
+ generate_auth_oauth2_handler(service_name, capitliazed_service_name, parent_path, force)
27
+ generate_auth_oauth2_schema(service_name, capitliazed_service_name, parent_path, force)
28
+ ##TODO: Add service to hyperpocket/auth/provider
29
+
30
+ def generate_server_auth(service_name, parent_path, force):
31
+ print(f"Generating server/auth for '{service_name}'.")
32
+ output_from_parsed_template = get_server_auth_oauth2_template().render(service_name=service_name)
33
+ output_path = parent_path / f'hyperpocket/hyperpocket/server/auth/{service_name}.py'
34
+ if not output_path.exists() or force:
35
+ with open(output_path, "w") as f:
36
+ f.write(output_from_parsed_template)
37
+
38
+ def generate_hyperpocket_auth_dir(service_name, parent_path, force):
39
+ if not os.path.exists(parent_path / f'hyperpocket/hyperpocket/auth/{service_name}'):
40
+ os.makedirs(parent_path / f'hyperpocket/hyperpocket/auth/{service_name}')
41
+
42
+ output_path = parent_path / f'hyperpocket/hyperpocket/auth/{service_name}/__init__.py'
43
+ if not output_path.exists() or force:
44
+ with open(output_path, "w") as f:
45
+ pass
46
+
47
+ def generate_auth_context(service_name, capitliazed_service_name, parent_path, force):
48
+ print(f"Generating auth/context for '{service_name}'.")
49
+ output_from_parsed_template = get_auth_context_template().render(
50
+ capitalized_service_name=capitliazed_service_name,
51
+ upper_service_name=service_name.upper()
52
+ )
53
+ output_path = parent_path / f'hyperpocket/hyperpocket/auth/{service_name}/context.py'
54
+ if not output_path.exists() or force:
55
+ with open(output_path, "w") as f:
56
+ f.write(output_from_parsed_template)
57
+
58
+ def generate_auth_oauth2_context(service_name, capitliazed_service_name, parent_path, force):
59
+ print(f"Generating auth/oauth2 context for '{service_name}'.")
60
+ output_from_parsed_template = get_auth_oauth2_context_template().render(
61
+ service_name = service_name,
62
+ capitalized_service_name=capitliazed_service_name,
63
+ )
64
+ output_path = parent_path / f'hyperpocket/hyperpocket/auth/{service_name}/oauth2_context.py'
65
+ if not output_path.exists() or force:
66
+ with open(output_path, "w") as f:
67
+ f.write(output_from_parsed_template)
68
+
69
+ def generate_auth_oauth2_handler(service_name, capitliazed_service_name, parent_path, force):
70
+ print(f"Generating auth/oauth2 handler for '{service_name}'.")
71
+ output_from_parsed_template = get_auth_oauth2_handler_template().render(
72
+ service_name = service_name,
73
+ auth_handler_name = service_name.replace('_','-'),
74
+ capitalized_service_name=capitliazed_service_name,
75
+ upper_service_name=service_name.upper()
76
+ )
77
+ output_path = parent_path / f'hyperpocket/hyperpocket/auth/{service_name}/oauth2_handler.py'
78
+ if not output_path.exists() or force:
79
+ with open(output_path, "w") as f:
80
+ f.write(output_from_parsed_template)
81
+
82
+ def generate_auth_oauth2_schema(service_name, capitliazed_service_name, parent_path, force):
83
+ print(f"Generating auth/oauth2 schema for '{service_name}'.")
84
+ output_from_parsed_template = get_auth_oauth2_schema_template().render(
85
+ capitalized_service_name=capitliazed_service_name,
86
+ )
87
+ output_path = parent_path / f'hyperpocket/hyperpocket/auth/{service_name}/oauth2_schema.py'
88
+ if not output_path.exists() or force:
89
+ with open(output_path, "w") as f:
90
+ f.write(output_from_parsed_template)
@@ -14,7 +14,8 @@ from hyperpocket.cli.codegen.auth import (
14
14
 
15
15
  @click.command()
16
16
  @click.argument("service_name", type=str)
17
- def create_token_auth_template(service_name):
17
+ @click.option("--force", is_flag=True, default=False)
18
+ def create_token_auth_template(service_name, force):
18
19
  ## Validate service_name
19
20
  if not service_name.islower() or not service_name.replace("_", "").isalpha():
20
21
  raise ValueError(
@@ -31,37 +32,39 @@ def create_token_auth_template(service_name):
31
32
  cwd = Path.cwd()
32
33
  parent_path = cwd.parent
33
34
 
34
- generate_server_auth(service_name, parent_path)
35
- generate_hyperpocket_auth_dir(service_name, parent_path)
36
- generate_auth_context(service_name, capitliazed_service_name, parent_path)
37
- generate_auth_token_context(service_name, capitliazed_service_name, parent_path)
38
- generate_auth_token_handler(service_name, capitliazed_service_name, parent_path)
39
- generate_auth_token_schema(service_name, capitliazed_service_name, parent_path)
35
+ generate_server_auth(service_name, parent_path, force)
36
+ generate_hyperpocket_auth_dir(service_name, parent_path, force)
37
+ generate_auth_context(service_name, capitliazed_service_name, parent_path, force)
38
+ generate_auth_token_context(service_name, capitliazed_service_name, parent_path, force)
39
+ generate_auth_token_handler(service_name, capitliazed_service_name, parent_path, force)
40
+ generate_auth_token_schema(service_name, capitliazed_service_name, parent_path, force)
40
41
  ##TODO: Add service to hyperpocket/auth/provider
41
42
 
42
43
 
43
- def generate_server_auth(service_name, parent_path):
44
+ def generate_server_auth(service_name, parent_path, force):
44
45
  print(f"Generating server/auth for '{service_name}'.")
45
46
  output_from_parsed_template = get_server_auth_token_template().render(
46
47
  service_name=service_name
47
48
  )
48
49
  output_path = parent_path / f"hyperpocket/hyperpocket/server/auth/{service_name}.py"
49
- with open(output_path, "w") as f:
50
- f.write(output_from_parsed_template)
50
+ if not output_path.exists() or force:
51
+ with open(output_path, "w") as f:
52
+ f.write(output_from_parsed_template)
51
53
 
52
54
 
53
- def generate_hyperpocket_auth_dir(service_name, parent_path):
55
+ def generate_hyperpocket_auth_dir(service_name, parent_path, force):
54
56
  if not os.path.exists(parent_path / f"hyperpocket/hyperpocket/auth/{service_name}"):
55
57
  os.makedirs(parent_path / f"hyperpocket/hyperpocket/auth/{service_name}")
56
58
 
57
59
  output_path = (
58
60
  parent_path / f"hyperpocket/hyperpocket/auth/{service_name}/__init__.py"
59
61
  )
60
- with open(output_path, "w"):
61
- pass
62
+ if not output_path.exists() or force:
63
+ with open(output_path, "w"):
64
+ pass
62
65
 
63
66
 
64
- def generate_auth_context(service_name, capitliazed_service_name, parent_path):
67
+ def generate_auth_context(service_name, capitliazed_service_name, parent_path, force):
65
68
  print(f"Generating auth/context for '{service_name}'.")
66
69
  output_from_parsed_template = get_auth_context_template().render(
67
70
  caplitalized_service_name=capitliazed_service_name,
@@ -70,11 +73,12 @@ def generate_auth_context(service_name, capitliazed_service_name, parent_path):
70
73
  output_path = (
71
74
  parent_path / f"hyperpocket/hyperpocket/auth/{service_name}/context.py"
72
75
  )
73
- with open(output_path, "w") as f:
74
- f.write(output_from_parsed_template)
76
+ if not output_path.exists() or force:
77
+ with open(output_path, "w") as f:
78
+ f.write(output_from_parsed_template)
75
79
 
76
80
 
77
- def generate_auth_token_context(service_name, capitliazed_service_name, parent_path):
81
+ def generate_auth_token_context(service_name, capitliazed_service_name, parent_path, force):
78
82
  print(f"Generating auth/token context for '{service_name}'.")
79
83
  output_from_parsed_template = get_auth_token_context_template().render(
80
84
  service_name=service_name,
@@ -83,11 +87,12 @@ def generate_auth_token_context(service_name, capitliazed_service_name, parent_p
83
87
  output_path = (
84
88
  parent_path / f"hyperpocket/hyperpocket/auth/{service_name}/token_context.py"
85
89
  )
86
- with open(output_path, "w") as f:
87
- f.write(output_from_parsed_template)
90
+ if not output_path.exists() or force:
91
+ with open(output_path, "w") as f:
92
+ f.write(output_from_parsed_template)
88
93
 
89
94
 
90
- def generate_auth_token_handler(service_name, capitliazed_service_name, parent_path):
95
+ def generate_auth_token_handler(service_name, capitliazed_service_name, parent_path, force):
91
96
  print(f"Generating auth/token handler for '{service_name}'.")
92
97
  output_from_parsed_template = get_auth_token_handler_template().render(
93
98
  service_name=service_name,
@@ -98,11 +103,12 @@ def generate_auth_token_handler(service_name, capitliazed_service_name, parent_p
98
103
  output_path = (
99
104
  parent_path / f"hyperpocket/hyperpocket/auth/{service_name}/token_handler.py"
100
105
  )
101
- with open(output_path, "w") as f:
102
- f.write(output_from_parsed_template)
106
+ if not output_path.exists() or force:
107
+ with open(output_path, "w") as f:
108
+ f.write(output_from_parsed_template)
103
109
 
104
110
 
105
- def generate_auth_token_schema(service_name, capitliazed_service_name, parent_path):
111
+ def generate_auth_token_schema(service_name, capitliazed_service_name, parent_path, force):
106
112
  print(f"Generating auth/token schema for '{service_name}'.")
107
113
  output_from_parsed_template = get_auth_token_schema_template().render(
108
114
  caplitalized_service_name=capitliazed_service_name,
@@ -110,5 +116,6 @@ def generate_auth_token_schema(service_name, capitliazed_service_name, parent_pa
110
116
  output_path = (
111
117
  parent_path / f"hyperpocket/hyperpocket/auth/{service_name}/token_schema.py"
112
118
  )
113
- with open(output_path, "w") as f:
114
- f.write(output_from_parsed_template)
119
+ if not output_path.exists() or force:
120
+ with open(output_path, "w") as f:
121
+ f.write(output_from_parsed_template)
@@ -1,13 +1,20 @@
1
1
  from .auth_context_template import get_auth_context_template
2
+ from .auth_oauth2_context_template import get_auth_oauth2_context_template
3
+ from .auth_oauth2_handler_template import get_auth_oauth2_handler_template
4
+ from .auth_oauth2_schema_template import get_auth_oauth2_schema_template
2
5
  from .auth_token_context_template import get_auth_token_context_template
3
6
  from .auth_token_handler_template import get_auth_token_handler_template
4
7
  from .auth_token_schema_template import get_auth_token_schema_template
5
- from .server_auth_template import get_server_auth_token_template
8
+ from .server_auth_template import get_server_auth_token_template, get_server_auth_oauth2_template
6
9
 
7
10
  __all__ = [
8
11
  "get_auth_context_template",
12
+ "get_auth_oauth2_context_template",
13
+ "get_auth_oauth2_handler_template",
14
+ "get_auth_oauth2_schema_template",
9
15
  "get_auth_token_context_template",
10
16
  "get_auth_token_handler_template",
11
17
  "get_auth_token_schema_template",
12
18
  "get_server_auth_token_template",
19
+ "get_server_auth_oauth2_template",
13
20
  ]
@@ -0,0 +1,38 @@
1
+ from jinja2 import Template
2
+
3
+ def get_auth_oauth2_context_template() -> Template:
4
+ return Template('''
5
+ from datetime import datetime, timedelta, timezone
6
+ from typing import Optional
7
+
8
+ from pydantic import Field
9
+
10
+ from hyperpocket.auth.{{ service_name }}.context import {{ capitalized_service_name }}AuthContext
11
+ from hyperpocket.auth.{{ service_name }}.oauth2_schema import {{ capitalized_service_name }}OAuth2Response
12
+
13
+
14
+ class {{ capitalized_service_name }}OAuth2AuthContext({{ capitalized_service_name }}AuthContext):
15
+ refresh_token: Optional[str] = Field(default=None, description="refresh token")
16
+
17
+ @classmethod
18
+ def from_{{ service_name }}_oauth2_response(cls, response: {{ capitalized_service_name }}OAuth2Response):
19
+ description = f'{{ capitalized_service_name }} OAuth2 Context logged in as a user {response.authed_user.id}'
20
+ now = datetime.now(tz=timezone.utc)
21
+
22
+ access_token = response.access_token
23
+ refresh_token = response.refresh_token
24
+ expires_in = response.expires_in
25
+
26
+ if expires_in:
27
+ expires_at = now + timedelta(seconds=response.authed_user.expires_in)
28
+ else:
29
+ expires_at = None
30
+
31
+ return cls(
32
+ access_token=access_token,
33
+ refresh_token=refresh_token,
34
+ expires_at=expires_at,
35
+ description=description,
36
+ detail=response,
37
+ )
38
+ ''')
@@ -0,0 +1,158 @@
1
+ from jinja2 import Template
2
+
3
+ def get_auth_oauth2_handler_template() -> Template:
4
+ return Template('''
5
+ from typing import Optional
6
+ from urllib.parse import urljoin, urlencode
7
+
8
+ import httpx
9
+
10
+ from hyperpocket.auth import AuthProvider
11
+ from hyperpocket.auth.context import AuthContext
12
+ from hyperpocket.auth.handler import AuthHandlerInterface
13
+ from hyperpocket.auth.{{ service_name }}.oauth2_context import {{ capitalized_service_name }}OAuth2AuthContext
14
+ from hyperpocket.auth.{{ service_name }}.oauth2_schema import {{ capitalized_service_name }}OAuth2Response, {{ capitalized_service_name }}OAuth2Request
15
+ from hyperpocket.config import config as config
16
+ from hyperpocket.futures import FutureStore
17
+
18
+
19
+ class {{ capitalized_service_name }}OAuth2AuthHandler(AuthHandlerInterface):
20
+ _{{ upper_service_name }}_OAUTH_URL: str = "" # e.g. "https://slack.com/oauth/v2/authorize"
21
+ _{{ upper_service_name }}_TOKEN_URL: str = "" # e.g. "https://slack.com/api/oauth.v2.access"
22
+
23
+ name: str = "{{ auth_handler_name }}-oauth2"
24
+ description: str = "This handler is used to authenticate users using the {{ capitalized_service_name }} OAuth2 authentication method."
25
+ scoped: bool = True
26
+
27
+ @staticmethod
28
+ def provider() -> AuthProvider:
29
+ return AuthProvider.{{ upper_service_name }}
30
+
31
+ @staticmethod
32
+ def provider_default() -> bool:
33
+ return True
34
+
35
+ @staticmethod
36
+ def recommended_scopes() -> set[str]:
37
+ """
38
+ This method returns a set of recommended scopes for the service.
39
+ If the service has a recommended scope, it should be returned here.
40
+ Example:
41
+ return {
42
+ "channels:history",
43
+ "channels:read",
44
+ "chat:write",
45
+ "groups:history",
46
+ "groups:read",
47
+ "im:history",
48
+ "mpim:history",
49
+ "reactions:read",
50
+ "reactions:write",
51
+ }
52
+ """
53
+ return set()
54
+
55
+ def prepare(self, auth_req: {{ capitalized_service_name }}OAuth2Request, thread_id: str, profile: str,
56
+ future_uid: str, *args, **kwargs) -> str:
57
+ redirect_uri = urljoin(
58
+ config.public_base_url + "/",
59
+ f"{config.callback_url_rewrite_prefix}/auth/{{ service_name }}/oauth2/callback",
60
+ )
61
+ print(f"redirect_uri: {redirect_uri}")
62
+ auth_url = self._make_auth_url(req=auth_req, redirect_uri=redirect_uri, state=future_uid)
63
+
64
+ FutureStore.create_future(future_uid, data={
65
+ "redirect_uri": redirect_uri,
66
+ "thread_id": thread_id,
67
+ "profile": profile,
68
+ })
69
+
70
+ return f'User needs to authenticate using the following URL: {auth_url}'
71
+
72
+ async def authenticate(self, auth_req: {{ capitalized_service_name }}OAuth2Request, future_uid: str, *args, **kwargs) -> AuthContext:
73
+ future_data = FutureStore.get_future(future_uid)
74
+ auth_code = await future_data.future
75
+
76
+ async with httpx.AsyncClient() as client:
77
+ resp = await client.post(
78
+ url=self._{{ upper_service_name }}_TOKEN_URL,
79
+ data={
80
+ 'client_id': auth_req.client_id,
81
+ 'client_secret': auth_req.client_secret,
82
+ 'code': auth_code,
83
+ 'redirect_uri': future_data.data["redirect_uri"],
84
+ }
85
+ )
86
+ if resp.status_code != 200:
87
+ raise Exception(f"failed to authenticate. status_code : {resp.status_code}")
88
+
89
+ resp_json = resp.json()
90
+ if resp_json["ok"] is False:
91
+ raise Exception(f"failed to authenticate. error : {resp_json['error']}")
92
+
93
+ resp_typed = {{ capitalized_service_name }}OAuth2Response(**resp_json)
94
+ return {{ capitalized_service_name }}OAuth2AuthContext.from_{{ service_name }}_oauth2_response(resp_typed)
95
+
96
+ async def refresh(self, auth_req: {{ capitalized_service_name }}OAuth2Request, context: AuthContext, *args, **kwargs) -> AuthContext:
97
+ {{ service_name }}_context: {{ capitalized_service_name }}OAuth2AuthContext = context
98
+ last_oauth2_resp: {{ capitalized_service_name }}OAuth2Response = {{ service_name }}_context.detail
99
+ refresh_token = {{ service_name }}_context.refresh_token
100
+
101
+ async with httpx.AsyncClient() as client:
102
+ resp = await client.post(
103
+ url=self._{{ upper_service_name }}_TOKEN_URL,
104
+ data={
105
+ 'client_id': config.auth.{{ service_name }}.client_id,
106
+ 'client_secret': config.auth.{{ service_name }}.client_secret,
107
+ 'grant_type': 'refresh_token',
108
+ 'refresh_token': refresh_token,
109
+ },
110
+ )
111
+
112
+ if resp.status_code != 200:
113
+ raise Exception(f"failed to refresh. status_code : {resp.status_code}")
114
+
115
+ resp_json = resp.json()
116
+ if resp_json["ok"] is False:
117
+ raise Exception(f"failed to refresh. status_code : {resp.status_code}")
118
+
119
+ if last_oauth2_resp.authed_user:
120
+ new_resp = last_oauth2_resp.model_copy(
121
+ update={
122
+ "authed_user": {{ capitalized_service_name }}OAuth2Response.AuthedUser(**{
123
+ **last_oauth2_resp.authed_user.model_dump(),
124
+ "access_token": resp_json["access_token"],
125
+ "refresh_token": resp_json["refresh_token"],
126
+ "expires_in": resp_json["expires_in"],
127
+ })
128
+ }
129
+ )
130
+ else:
131
+ new_resp = last_oauth2_resp.model_copy(
132
+ update={
133
+ **last_oauth2_resp.model_dump(),
134
+ "access_token": resp_json["access_token"],
135
+ "refresh_token": resp_json["refresh_token"],
136
+ "expires_in": resp_json["expires_in"],
137
+ }
138
+ )
139
+
140
+ return {{ capitalized_service_name }}OAuth2AuthContext.from_{{ service_name }}_oauth2_response(new_resp)
141
+
142
+ def _make_auth_url(self, req: {{ capitalized_service_name }}OAuth2Request, redirect_uri: str, state: str):
143
+ params = {
144
+ "user_scope": ','.join(req.auth_scopes),
145
+ "client_id": req.client_id,
146
+ "redirect_uri": redirect_uri,
147
+ "state": state,
148
+ }
149
+ auth_url = f"{self._{{ upper_service_name }}_OAUTH_URL}?{urlencode(params)}"
150
+ return auth_url
151
+
152
+ def make_request(self, auth_scopes: Optional[list[str]] = None, **kwargs) -> {{ capitalized_service_name }}OAuth2Request:
153
+ return {{ capitalized_service_name }}OAuth2Request(
154
+ auth_scopes=auth_scopes,
155
+ client_id=config.auth.{{ service_name }}.client_id,
156
+ client_secret=config.auth.{{ service_name }}.client_secret,
157
+ )
158
+ ''')
@@ -0,0 +1,20 @@
1
+ from jinja2 import Template
2
+
3
+ def get_auth_oauth2_schema_template() -> Template:
4
+ return Template('''
5
+ from typing import Optional
6
+
7
+ from pydantic import BaseModel
8
+
9
+ from hyperpocket.auth.schema import AuthenticateRequest, AuthenticateResponse
10
+
11
+
12
+ class {{ capitalized_service_name }}OAuth2Request(AuthenticateRequest):
13
+ client_id: str
14
+ client_secret: str
15
+
16
+ class {{ capitalized_service_name }}OAuth2Response(AuthenticateResponse):
17
+ access_token: str
18
+ expires_in: int
19
+ refresh_token: Optional[str] = Field(default=None)
20
+ ''')
@@ -2,7 +2,7 @@ from jinja2 import Template
2
2
 
3
3
 
4
4
  def get_server_auth_token_template() -> Template:
5
- return Template("""
5
+ return Template('''
6
6
  from fastapi import APIRouter
7
7
  from starlette.responses import HTMLResponse
8
8
  from hyperpocket.futures import FutureStore
@@ -15,5 +15,36 @@ async def {{ service_name }}_token_callback(state: str, token: str):
15
15
  FutureStore.resolve_future(state, token)
16
16
  except ValueError:
17
17
  return HTMLResponse(content="failed")
18
- return HTMLResponse(content="success")
19
- """)
18
+ return HTMLResponse(content="success")
19
+ ''')
20
+
21
+ def get_server_auth_oauth2_template() -> Template:
22
+ return Template('''
23
+ from fastapi import APIRouter
24
+ from starlette.responses import HTMLResponse
25
+ from hyperpocket.futures import FutureStore
26
+
27
+ {{ service_name }}_auth_router = APIRouter(
28
+ prefix="/{{ service_name }}"
29
+ )
30
+
31
+
32
+ @{{ service_name }}_auth_router.get("/oauth2/callback")
33
+ async def {{ service_name }}_oauth2_callback(state: str, code: str):
34
+ try:
35
+ FutureStore.resolve_future(state, code)
36
+ except ValueError:
37
+ return HTMLResponse(content="failed")
38
+
39
+ return HTMLResponse(content="success")
40
+
41
+
42
+ @{{ service_name }}_auth_router.get("/token/callback")
43
+ async def {{ service_name }}_token_callback(state: str, token: str):
44
+ try:
45
+ FutureStore.resolve_future(state, token)
46
+ except ValueError:
47
+ return HTMLResponse(content="failed")
48
+
49
+ return HTMLResponse(content="success")
50
+ ''')