async def test_oauth1_authorize(): oauth = OAuth() app = AsyncPathMapDispatch({ '/request-token': { 'body': 'oauth_token=foo&oauth_verifier=baz' }, '/token': { 'body': 'oauth_token=a&oauth_token_secret=b' }, }) client = oauth.register('dev', client_id='dev', client_secret='dev', request_token_url='https://i.b/request-token', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={ 'app': app, }) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) resp = await client.authorize_redirect(req, 'https://b.com/bar') assert resp.status_code == 302 url = resp.headers.get('Location') assert 'oauth_token=foo' in url req_token = req.session.get('_dev_authlib_request_token_') assert req_token is not None req.scope['query_string'] = 'oauth_token=foo&oauth_verifier=baz' token = await client.authorize_access_token(req) assert token['oauth_token'] == 'a'
async def test_with_fetch_token_in_register(): async def fetch_token(request): return {'access_token': 'dev', 'token_type': 'bearer'} dispatch = PathMapDispatch({ '/user': {'body': {'sub': '123'}} }) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', fetch_token=fetch_token, client_kwargs={ 'dispatch': dispatch, } ) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) resp = await client.get('/user', request=req) assert resp.json()['sub'] == '123'
async def test_oauth2_authorize_with_metadata(): oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', ) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) with pytest.raises(RuntimeError): await client.create_authorization_url(req) app = AsyncPathMapDispatch({ '/.well-known/openid-configuration': { 'body': { 'authorization_endpoint': 'https://i.b/authorize' } } }) client = oauth.register( 'dev2', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', server_metadata_url='https://i.b/.well-known/openid-configuration', client_kwargs={ 'app': app, }) resp = await client.authorize_redirect(req, 'https://b.com/bar') assert resp.status_code == 302
async def test_force_fetch_jwks_uri(): secret_keys = read_file_path('jwks_private.json') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, secret_keys, alg='RS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) app = AsyncPathMapDispatch({ '/jwks': {'body': read_file_path('jwks_public.json')} }) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, jwks_uri='https://i.b/jwks', issuer='https://i.b', client_kwargs={ 'app': app, } ) req_scope = {'type': 'http', 'session': {'_dev_authlib_nonce_': 'n'}} req = Request(req_scope) token['id_token'] = id_token user = await client.parse_id_token(req, token) assert user.sub == '123'
async def test_oauth2_authorize(): oauth = OAuth() app = AsyncPathMapDispatch({'/token': {'body': get_bearer_token()}}) client = oauth.register('dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={ 'app': app, }) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) resp = await client.authorize_redirect(req, 'https://b.com/bar') assert resp.status_code == 302 url = resp.headers.get('Location') assert 'state=' in url state = req.session.get('_dev_authlib_state_') assert state is not None req_scope.update({ 'path': '/', 'query_string': f'code=a&state={state}', 'session': req.session, }) req = Request(req_scope) token = await client.authorize_access_token(req) assert token['access_token'] == 'a'
async def run_fetch_userinfo(payload, compliance_fix=None): oauth = OAuth() async def fetch_token(request): return get_bearer_token() app = AsyncPathMapDispatch({ '/userinfo': {'body': payload} }) client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=fetch_token, userinfo_endpoint='https://i.b/userinfo', userinfo_compliance_fix=compliance_fix, client_kwargs={ 'app': app, } ) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) user = await client.userinfo(request=req) assert user.sub == '123'
async def test_runtime_error_fetch_jwks_uri(): key = jwk.dumps('secret', 'oct', kid='f') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, issuer='https://i.b', id_token_signing_alg_values_supported=['HS256'], ) req_scope = {'type': 'http', 'session': {'_dev_authlib_nonce_': 'n'}} req = Request(req_scope) token['id_token'] = id_token with pytest.raises(RuntimeError): await client.parse_id_token(req, token)
def test_register_remote_app(): oauth = OAuth() with pytest.raises(AttributeError): assert oauth.dev.name == 'dev' oauth.register( 'dev', client_id='dev', client_secret='dev', ) assert oauth.dev.name == 'dev' assert oauth.dev.client_id == 'dev'
def init(app): import json from fastapi import FastAPI from starlette.config import Config from starlette.requests import Request from starlette.middleware.sessions import SessionMiddleware from starlette.responses import HTMLResponse, RedirectResponse from authlib.integrations.starlette_client import OAuth, OAuthError from .config import get_sso_config, GOOGLE_CONF_URL from meerschaum import get_connector prepend_path = get_sso_config('prepend') google_client_id = get_sso_config('google', 'id') google_client_secret = get_sso_config('google', 'secret') google_callback_url = get_sso_config('google', 'callback') oauth = OAuth() oauth.register( name='google', client_id=google_client_id, client_secret=google_client_secret, server_metadata_url=GOOGLE_CONF_URL, client_kwargs={ 'scope': 'openid email profile', }, ) app.add_middleware(SessionMiddleware, secret_key=google_client_secret) sso_google_path = prepend_path + '/sso/google' @app.get(sso_google_path) async def login_sso(request: Request): redirect_uri = google_callback_url return await oauth.google.authorize_redirect(request, redirect_uri) @app.get(sso_google_path + '/callback') async def google_callback(request: Request): token = await oauth.google.authorize_access_token(request) user = await oauth.google.parse_id_token(request, token) conn = get_connector('sql', 'wedding_s') q = f"SELECT address FROM \"people-with-contact-info\" WHERE email = '{user.email}'" address = conn.value(q) response = RedirectResponse('/login_success') response.set_cookie(key='login_name', value=user.name) response.set_cookie(key='login_email', value=user.email) response.set_cookie(key='login_address', value=address) return response
async def test_request_withhold_token(): oauth = OAuth() app = AsyncPathMapDispatch({'/user': {'body': {'sub': '123'}}}) client = oauth.register("dev", client_id="dev", client_secret="dev", api_base_url="https://i.b/api", access_token_url="https://i.b/token", authorize_url="https://i.b/authorize", client_kwargs={ 'app': app, }) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) resp = await client.get('/user', request=req, withhold_token=True) assert resp.json()['sub'] == '123'
async def test_oauth2_authorize_code_challenge(): dispatch = PathMapDispatch({ '/token': {'body': get_bearer_token()} }) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={ 'code_challenge_method': 'S256', 'dispatch': dispatch, }, ) req_scope = {'type': 'http', 'session': {}} req = Request(req_scope) resp = await client.authorize_redirect(req, redirect_uri='https://b.com/bar') assert resp.status_code == 302 url = resp.headers.get('Location') assert 'code_challenge=' in url assert 'code_challenge_method=S256' in url state = req.session['_dev_authlib_state_'] assert state is not None verifier = req.session['_dev_authlib_code_verifier_'] assert verifier is not None req_scope.update( { 'path': '/', 'query_string': 'code=a&state={}'.format(state).encode(), 'session': req.session, } ) req = Request(req_scope) token = await client.authorize_access_token(req) assert token['access_token'] == 'a'
async def test_parse_id_token(): key = jwk.dumps('secret', 'oct', kid='f') token = get_bearer_token() id_token = generate_id_token( token, {'sub': '123'}, key, alg='HS256', iss='https://i.b', aud='dev', exp=3600, nonce='n', ) oauth = OAuth() client = oauth.register( 'dev', client_id='dev', client_secret='dev', fetch_token=get_bearer_token, jwks={'keys': [key]}, issuer='https://i.b', id_token_signing_alg_values_supported=['HS256', 'RS256'], ) req_scope = {'type': 'http', 'session': {'_dev_authlib_nonce_': 'n'}} req = Request(req_scope) user = await client.parse_id_token(req, token) assert user is None token['id_token'] = id_token user = await client.parse_id_token(req, token) assert user.sub == '123' claims_options = {'iss': {'value': 'https://i.b'}} user = await client.parse_id_token(req, token, claims_options) assert user.sub == '123' with pytest.raises(InvalidClaimError): claims_options = {'iss': {'value': 'https://i.c'}} await client.parse_id_token(req, token, claims_options)
async def test_oauth2_authorize_access_denied(): oauth = OAuth() app = AsyncPathMapDispatch({'/token': {'body': get_bearer_token()}}) client = oauth.register('dev', client_id='dev', client_secret='dev', api_base_url='https://i.b/api', access_token_url='https://i.b/token', authorize_url='https://i.b/authorize', client_kwargs={ 'app': app, }) req = Request({ 'type': 'http', 'session': {}, 'path': '/', 'query_string': 'error=access_denied&error_description=Not+Allowed', }) with pytest.raises(OAuthError): await client.authorize_access_token(req)
import json from fastapi import FastAPI, APIRouter from starlette.config import Config from starlette.requests import Request #from starlette.middleware.sessions import SessionMiddleware from starlette.responses import HTMLResponse, RedirectResponse from authlib.integrations.starlette_client import OAuth, OAuthError from app.configs import googleinfo router = APIRouter() #router.add_middleware(SessionMiddleware, secret_key="!secret") def google_config(): return googleinfo.setting() # config = Config('.env') oauth = OAuth(google_config) print(oauth) CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration' oauth.register( name='google', server_metadata_url=CONF_URL, client_kwargs={ 'scope': 'openid email profile' } ) @router.get('/') async def homepage(request: Request): user = request.session.get('user')
# Make the avatar a full URL if provided if "avatar" in data: params[ "picture" ] = f"https://cdn.discordapp.com/avatars/{data['id']}/{data['avatar']}.png" return params # And remap the configuration to be compatible with Starlette environ["DISCORD_CLIENT_ID"] = environ["API_DISCORD_CLIENT_ID"] environ["DISCORD_CLIENT_SECRET"] = environ["API_DISCORD_CLIENT_SECRET"] config = Config(".env") # Register the discord auth provider oauth = OAuth(config) oauth.register( "discord", api_base_url="https://discord.com/api/", access_token_url="https://discord.com/api/oauth2/token", authorize_url="https://discord.com/api/oauth2/authorize", userinfo_endpoint="https://discord.com/api/users/%40me", userinfo_compliance_fix=normalize_userinfo, client_kwargs={ "token_endpoint_auth_method": "client_secret_post", "scope": "identify guilds", }, ) def get_discord_client():
# Copyright 2020 QuantStack # Distributed under the terms of the Modified BSD License. from starlette.responses import RedirectResponse from fastapi import APIRouter, Request from authlib.integrations.starlette_client import OAuth from .database import SessionLocal from .dao_github import get_user_by_github_identity from quetz import config import json import uuid router = APIRouter() oauth = OAuth() # Register the app here: https://github.com/settings/applications/new oauth.register( name='github', client_id=config.github_client_id, client_secret=config.github_client_secret, access_token_url='https://github.com/login/oauth/access_token', access_token_params=None, authorize_url='https://github.com/login/oauth/authorize', authorize_params=None, api_base_url='https://api.github.com/', client_kwargs={'scope': 'user:email'}, ) async def validate_token(token): # identity = get_identity(db, identity_id)
from authlib.integrations.starlette_client import OAuth from fastapi.responses import HTMLResponse from starlette.config import Config from starlette.middleware.sessions import SessionMiddleware from loginpass import create_fastapi_routes, Twitter, GitHub, Google from fastapi import FastAPI app = FastAPI() config = Config(".env") oauth = OAuth(config) app.add_middleware(SessionMiddleware, secret_key=config.get("SECRET_KEY")) backends = [Twitter, GitHub, Google] @app.get("/", response_class=HTMLResponse) async def root() -> str: tpl = '<li><a href="/login/{}">{}</a></li>' lis = [tpl.format(b.NAME, b.NAME) for b in backends] return "<ul>{}</ul>".format("".join(lis)) def handle_authorize(remote, token, user_info, request): return user_info router = create_fastapi_routes(backends, oauth, handle_authorize)
def test_register_with_overwrite(): config = Config(environ={'DEV_CLIENT_ID': 'dev'}) oauth = OAuth(config) oauth.register('dev', client_id='not-dev', overwrite=True) assert oauth.dev.name == 'dev' assert oauth.dev.client_id == 'dev'
from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse from starlette.config import Config from starlette.middleware.sessions import SessionMiddleware from authlib.integrations.starlette_client import OAuth app = FastAPI() app.add_middleware(SessionMiddleware, secret_key="!secret") config = Config('.env') oauth = OAuth(config) redirect_uri_google = "https://b553a103b824.ngrok.io/auth-google" redirect_uri_facebook = "https://b553a103b824.ngrok.io/auth-facebook" oauth.register( name='google', server_metadata_url='https://accounts.google.com/.well-known/openid-configuration', client_kwargs={'scope': 'openid email profile'} ) oauth.register( name='facebook', api_base_url='https://graph.facebook.com/v7.0/', access_token_url='https://graph.facebook.com/v7.0/oauth/access_token', authorize_url='https://www.facebook.com/v7.0/dialog/oauth', client_kwargs={'scope': 'email public_profile'}, ) @app.get('/') async def homepage(request: Request):
async def fetch_token(name: str, request: Request) -> dict: logging.debug('fetch_token') logging.debug(name) for token in tokens: if token.name == name: return token.dict(exclude={'name'}) token = Token(name=name, token_type='Bearer', access_token='cafe', expires_at=1) return token.dict(exclude={'name'}) async def update_token( name: str, token: OAuth2Token, refresh_token: Optional[str] = None, access_token: Optional[str] = None, expires_at: Optional[int] = None, ) -> None: for i, _token in enumerate(tokens): if _token.name == name and _token.access_token == access_token: tokens[i] = Token(name=name, **token) return tokens.append(Token(name=name, **token)) oauth = OAuth(fetch_token=fetch_token, update_token=update_token)
def test_register_with_config(): config = Config(environ={'DEV_CLIENT_ID': 'dev'}) oauth = OAuth(config) oauth.register('dev') assert oauth.dev.name == 'dev' assert oauth.dev.client_id == 'dev'
from authlib.integrations.starlette_client import OAuth from starlette.responses import JSONResponse from users.domain import services from users.domain.services.social_login import SocialAccount, SocialLoginInput from users.settings import GOOGLE_AUTH_CLIENT_ID, GOOGLE_AUTH_CLIENT_SECRET oauth = OAuth() CONF_URL = "https://accounts.google.com/.well-known/openid-configuration" oauth.register( name="google", server_metadata_url=CONF_URL, client_id=str(GOOGLE_AUTH_CLIENT_ID), client_secret=str(GOOGLE_AUTH_CLIENT_SECRET), client_kwargs={"scope": "openid email profile"}, ) async def google_login(request): redirect_uri = request.url_for("auth") return await oauth.google.authorize_redirect(request, redirect_uri) async def google_login_auth(request): token = await oauth.google.authorize_access_token(request) user = await oauth.google.parse_id_token(request, token) if not user["email_verified"]: return JSONResponse({"error": "Email not verified"}, status_code=400)
import aiohttp as aiohttp from authlib.integrations.starlette_client import OAuth from authlib.oauth2.rfc6749 import OAuth2Token from fastapi import Depends, APIRouter from starlette.requests import Request from starlette.responses import RedirectResponse from app.libs.config import Config from app.model import User SESSION_USER_KEY = 'user' router = APIRouter() config = Config() oauth = OAuth() oauth.register( name='discord', client_id=config.get_client_id(), client_secret=config.get_client_secret(), authorize_url='https://discordapp.com/api/oauth2/authorize', access_token_url='https://discordapp.com/api/oauth2/token', client_kwargs={'scope': ' '.join(['identify', 'guilds', 'email'])}, ) def get_current_user(request: Request) -> Optional[User]: if SESSION_USER_KEY not in request.session: return kwargs = request.session[SESSION_USER_KEY] if time.time() >= kwargs.get('expires_at', 0):
from authlib.integrations.starlette_client import OAuth, OAuthError from fastapi import Depends, HTTPException from fastapi.responses import RedirectResponse from sqlalchemy.sql import select from starlette.requests import Request import aurweb.config import aurweb.db from aurweb import util from aurweb.l10n import get_translator_for_request from aurweb.schema import Bans, Sessions, Users router = fastapi.APIRouter() oauth = OAuth() oauth.register( name="sso", server_metadata_url=aurweb.config.get("sso", "openid_configuration"), client_kwargs={"scope": "openid"}, client_id=aurweb.config.get("sso", "client_id"), client_secret=aurweb.config.get("sso", "client_secret"), ) @router.get("/sso/login") async def login(request: Request, redirect: str = None): """ Redirect the user to the SSO provider’s login page. We specify prompt=login to force the user to input their credentials even
from ..configs import configs from ..exceptions import NotAuthorizedError, TokenSignatureInvalid, TokenSignatureExpired, UserNotApprovedError from ..models import User, get_doc FRONTEND_APP = 'brickserver_frontend' A = 'A' # actuatable W = 'W' # writable R = 'R' # readable O = 'O' # owning auth_scheme = HTTPBearer(bearerFormat='JWT') if configs['auth'].get('oauth_connections', None): google_config = configs['auth']['oauth_connections']['google'] oauth = OAuth() oauth.register( name='google', client_id=google_config['client_id'], client_secret=google_config['client_secret'], api_base_url=google_config['api_base_url'], request_token_url=None, request_token_params={ 'scope': 'email openid profile', 'access_type': 'offline', 'prompt': 'consent', }, access_token_url=google_config['access_token_url'], authorize_url=google_config['authorize_url'], client_kwargs=google_config['client_kwargs'], jwks_uri=google_config['jwks_uri'],
get_medication_views, update_medication_tap, get_unused_medication_tap_blocks_for_day, add_medication from views import MainTemplateData templates = Jinja2Templates(directory="templates/") log = logging.getLogger(__name__) app = FastAPI() config = Config(".env") if config.get("GOOGLE_CLIENT_ID", default=None) is None \ or config.get("GOOGLE_CLIENT_SECRET", default=None) is None \ or config.get("SESSION_SECRET", default=None) is None: raise Exception( "Configuration error. GOOGLE_CLIENT_ID or GOOGLE_CLIENT_SECRET or SESSION_SECRET not specified." ) oauth = OAuth(config) oauth.register(name='google', server_metadata_url= 'https://accounts.google.com/.well-known/openid-configuration', client_kwargs={'scope': 'openid email profile'}) app.add_middleware(SessionMiddleware, secret_key=config.get("SESSION_SECRET")) @app.get("/") async def homepage(request: Request, target_date: Optional[datetime.date] = None): user = request.session.get('user') if target_date is None: target_date = datetime.date.today() if user is None:
# ALT: https://docs.authlib.org/en/latest/specs/rfc7523.html#jwt-grant-type # https://docs.authlib.org/en/latest/client/starlette.html # Overview: auth to keycloak to interconnect microservices on backend from authlib.oauth2.rfc6749.grants import (ClientCredentialsGrant, RefreshTokenGrant) from authlib.integrations.starlette_client import OAuth oauth = OAuth() oauth.register( name='webapp', client_id='{{ your-github-client-id }}', client_secret='{{ your-github-client-secret }}', access_token_url='https://github.com/login/oauth/access_token', authorize_url='https://github.com/login/oauth/authorize', api_base_url='https://api.github.com/', client_kwargs={'scope': 'user:email'}, ) class ClientCredentials(ClientCredentialsGrant): TOKEN_ENDPOINT_AUTH_METHODS = ['client_secret_basic', 'client_secret_post'] class RefreshToken(RefreshTokenGrant): TOKEN_ENDPOINT_AUTH_METHODS = ['client_secret_basic', 'client_secret_post'] def authenticate_refresh_token(self, refresh_token): item = Token.query.filter_by(refresh_token=refresh_token).first()
from starlette.responses import HTMLResponse from starlette.authentication import requires, has_required_scope # NOQA from authlib.integrations.starlette_client import OAuth from helpdesk.config import OPENID_PRIVIDERS, oauth_username_func from helpdesk.models.user import User from helpdesk.libs.rest import jsonize from . import bp logger = logging.getLogger(__name__) oauth_clients = {} for provider, info in OPENID_PRIVIDERS.items(): _auth = OAuth() _auth.register(provider, **info) client = _auth.create_client(provider) oauth_clients[provider] = client @bp.route('/oauth/{provider}') async def oauth(request): provider = request.path_params.get('provider', '') client = oauth_clients[provider] # FIXME: url_for behind proxy url_path = request['router'].url_path_for('auth:callback', provider=provider) server = request["server"] if server[1] in (80, 443): base_url = f"{request['scheme']}://{server[0]}{request.get('app_root_path', '/')}"
# in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. from authlib.integrations.starlette_client import OAuth from distrobuild.settings import settings oauth = OAuth() oauth.register( name="oidc", client_id=settings.oidc_client_id, client_secret=settings.oidc_client_secret, server_metadata_url= f"{settings.oidc_issuer}/.well-known/openid-configuration", client_kwargs={"scope": f"openid profile groups {settings.oidc_scopes}"}) oidc = oauth.oidc
# along with this program. If not, see <https://www.gnu.org/licenses/>. import os import re from authlib.common.errors import AuthlibBaseError from authlib.integrations.starlette_client import OAuth from fastapi import APIRouter, Request, HTTPException from fastapi.responses import RedirectResponse, JSONResponse from sentry_sdk import capture_exception from web.config import FRONTEND_URL, app from web.data import database, get_session_id, logger, update_web_user, verify_session router = APIRouter(prefix="/user", tags=["user"]) oauth = OAuth() REL_REGEX = r"/[^/](?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))*" relative_url_regex = re.compile(REL_REGEX) DISCORD_CLIENT_SECRET = os.getenv("DISCORD_CLIENT_SECRET") oauth.register( name="discord", client_id=601917808137338900, client_secret=DISCORD_CLIENT_SECRET, access_token_url="https://discord.com/api/oauth2/token", access_token_params=None, authorize_url="https://discord.com/api/oauth2/authorize", authorize_params=None, api_base_url="https://discord.com/api/", client_kwargs={