Skip to content

Commit

Permalink
feat: added Yandex SSO provider (#146)
Browse files Browse the repository at this point in the history
* feat: added Yandex SSO provider

* chore: fixed module docstring for yandex example

* chore: removed useless imports, updated imports sort

* docs: added contribute author for YandexSSO
  • Loading branch information
akimrx authored Apr 2, 2024
1 parent 2362a6f commit 0062371
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ I tend to process Pull Requests faster when properly caffeinated 😉.
- Gitlab (by Alessandro Pischedda) - [Cereal84](https://github.com/Cereal84)
- Line (by Jimmy Yeh) - [jimmyyyeh](https://github.com/jimmyyyeh)
- LinkedIn (by Alessandro Pischedda) - [Cereal84](https://github.com/Cereal84)
- Yandex (by Akim Faskhutdinov) – [akimrx](https://github.com/akimrx)

See [Contributing](#contributing) for a guide on how to contribute your own login provider.

Expand Down
38 changes: 38 additions & 0 deletions examples/yandex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""Yandex Login Example
"""

import os
import uvicorn
from fastapi import FastAPI, Request
from fastapi_sso.sso.yandex import YandexSSO

CLIENT_ID = os.environ["CLIENT_ID"]
CLIENT_SECRET = os.environ["CLIENT_SECRET"]

app = FastAPI()

sso = YandexSSO(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
redirect_uri="http://localhost:5000/auth/callback",
allow_insecure_http=True,
)


@app.get("/auth/login")
async def auth_init():
"""Initialize auth and redirect"""
with sso:
return await sso.get_login_redirect()


@app.get("/auth/callback")
async def auth_callback(request: Request):
"""Verify login"""
with sso:
user = await sso.verify_and_process(request)
return user


if __name__ == "__main__":
uvicorn.run(app="examples.yandex:app", host="127.0.0.1", port=5000)
44 changes: 44 additions & 0 deletions fastapi_sso/sso/yandex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Yandex SSO Login Helper
"""

from typing import TYPE_CHECKING, Optional

from fastapi_sso.sso.base import DiscoveryDocument, OpenID, SSOBase

if TYPE_CHECKING:
import httpx # pragma: no cover


class YandexSSO(SSOBase):
"""Class providing login using Yandex OAuth."""

provider = "yandex"
scope = ["login:email", "login:info", "login:avatar"]
avatar_url = "https://avatars.yandex.net/get-yapic"

async def get_discovery_document(self) -> DiscoveryDocument:
"""Override the discovery document method to return Yandex OAuth endpoints."""

return {
"authorization_endpoint": "https://oauth.yandex.ru/authorize",
"token_endpoint": "https://oauth.yandex.ru/token",
"userinfo_endpoint": "https://login.yandex.ru/info",
}

async def openid_from_response(self, response: dict, session: Optional["httpx.AsyncClient"] = None) -> OpenID:
"""Converts Yandex user info response to OpenID object."""

picture = None

if (avatar_id := response.get("default_avatar_id")) is not None:
picture = f"{self.avatar_url}/{avatar_id}/islands-200"

return OpenID(
email=response.get("default_email"),
display_name=response.get("display_name"),
provider=self.provider,
id=response.get("id"),
first_name=response.get("first_name"),
last_name=response.get("last_name"),
picture=picture,
)
20 changes: 20 additions & 0 deletions tests/test_openid_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from fastapi_sso.sso.github import GithubSSO
from fastapi_sso.sso.fitbit import FitbitSSO
from fastapi_sso.sso.facebook import FacebookSSO
from fastapi_sso.sso.yandex import YandexSSO

sso_mapping: Dict[Type[SSOBase], Tuple[Dict[str, Any], OpenID]] = {
TwitterSSO: (
Expand Down Expand Up @@ -96,6 +97,25 @@
picture="https://myimage",
),
),
YandexSSO: (
{
"id": "test",
"display_name": "test",
"first_name": "Test",
"last_name": "User",
"default_email": "test@example.com",
"default_avatar_id": "123456",
},
OpenID(
email="test@example.com",
first_name="Test",
last_name="User",
display_name="test",
id="test",
provider="yandex",
picture="https://avatars.yandex.net/get-yapic/123456/islands-200",
),
),
}


Expand Down
2 changes: 2 additions & 0 deletions tests/test_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from fastapi_sso.sso.notion import NotionSSO
from fastapi_sso.sso.linkedin import LinkedInSSO
from fastapi_sso.sso.twitter import TwitterSSO
from fastapi_sso.sso.yandex import YandexSSO

GenericProvider = create_provider(
name="generic",
Expand All @@ -49,6 +50,7 @@
NotionSSO,
LinkedInSSO,
TwitterSSO,
YandexSSO,
)

# Run all tests for each of the listed providers
Expand Down

0 comments on commit 0062371

Please sign in to comment.