|
1 | 1 | import jwt
|
2 | 2 | import utils
|
3 |
| -import typing |
4 | 3 | import config
|
5 |
| -import aiohttp |
6 | 4 |
|
7 | 5 | from api.models import User, Token
|
8 | 6 |
|
9 |
| -from pydantic import BaseModel |
10 | 7 | from fastapi.params import Param
|
11 | 8 | from fastapi import APIRouter, Request
|
12 | 9 | from datetime import datetime, timedelta
|
13 |
| -from urllib.parse import quote_plus, urlparse |
14 | 10 | from fastapi.responses import RedirectResponse
|
| 11 | +from .models import CallbackBody, CallbackResponse |
| 12 | +from .helpers import ( |
| 13 | + SCOPES, |
| 14 | + get_user, |
| 15 | + get_redirect, |
| 16 | + is_valid_url, |
| 17 | + exchange_code, |
| 18 | + format_scopes, |
| 19 | +) |
15 | 20 |
|
16 | 21 | router = APIRouter(prefix="/auth")
|
17 | 22 |
|
18 | 23 |
|
19 |
| -DISCORD_ENDPOINT = "https://discord.com/api" |
20 |
| -SCOPES = ["identify"] |
21 |
| - |
22 |
| - |
23 |
| -class CallbackResponse(BaseModel): |
24 |
| - token: str |
25 |
| - exp: datetime |
26 |
| - |
27 |
| - |
28 |
| -class CallbackBody(BaseModel): |
29 |
| - code: str |
30 |
| - callback: str |
31 |
| - |
32 |
| - |
33 |
| -async def exchange_code( |
34 |
| - *, code: str, scope: str, redirect_uri: str, grant_type: str = "authorization_code" |
35 |
| -) -> typing.Tuple[dict, int]: |
36 |
| - """Exchange discord oauth code for access and refresh tokens.""" |
37 |
| - async with aiohttp.ClientSession() as session: |
38 |
| - async with session.post( |
39 |
| - "%s/v6/oauth2/token" % DISCORD_ENDPOINT, |
40 |
| - data=dict( |
41 |
| - code=code, |
42 |
| - scope=scope, |
43 |
| - grant_type=grant_type, |
44 |
| - redirect_uri=redirect_uri, |
45 |
| - client_id=config.discord_client_id(), |
46 |
| - client_secret=config.discord_client_secret(), |
47 |
| - ), |
48 |
| - headers={"Content-Type": "application/x-www-form-urlencoded"}, |
49 |
| - ) as response: |
50 |
| - return await response.json(), response.status |
51 |
| - |
52 |
| - |
53 |
| -async def get_user(access_token: str) -> dict: |
54 |
| - """Coroutine to fetch User data from discord using the users `access_token`""" |
55 |
| - async with aiohttp.ClientSession() as session: |
56 |
| - async with session.get( |
57 |
| - "%s/v6/users/@me" % DISCORD_ENDPOINT, |
58 |
| - headers={"Authorization": "Bearer %s" % access_token}, |
59 |
| - ) as response: |
60 |
| - return await response.json() |
61 |
| - |
62 |
| - |
63 |
| -def format_scopes(scopes: typing.List[str]) -> str: |
64 |
| - """Format a list of scopes.""" |
65 |
| - return " ".join(scopes) |
66 |
| - |
67 |
| - |
68 |
| -def get_redirect(callback: str, scopes: typing.List[str]) -> str: |
69 |
| - """Generates the correct oauth link depending on our provided arguments.""" |
70 |
| - return ( |
71 |
| - "{BASE}/oauth2/authorize?response_type=code" |
72 |
| - "&client_id={client_id}" |
73 |
| - "&scope={scopes}" |
74 |
| - "&redirect_uri={redirect_uri}" |
75 |
| - "&prompt=consent" |
76 |
| - ).format( |
77 |
| - BASE=DISCORD_ENDPOINT, |
78 |
| - scopes=format_scopes(scopes), |
79 |
| - redirect_uri=quote_plus(callback), |
80 |
| - client_id=config.discord_client_id(), |
81 |
| - ) |
82 |
| - |
83 |
| - |
84 |
| -def is_valid_url(string: str) -> bool: |
85 |
| - """Returns boolean describing if the provided string is a url""" |
86 |
| - result = urlparse(string) |
87 |
| - return all((result.scheme, result.netloc)) |
88 |
| - |
89 |
| - |
90 | 24 | @router.get(
|
91 | 25 | "/discord/redirect",
|
92 | 26 | tags=["auth"],
|
|
0 commit comments