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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ ''')