Module aiogithubapi.legacy.device
Class for OAuth device flow authentication.
https://docs.github.com/en/developers/apps/authorizing-oauth-apps#device-flow
Expand source code
"""
Class for OAuth device flow authentication.
https://docs.github.com/en/developers/apps/authorizing-oauth-apps#device-flow
"""
from __future__ import annotations
import asyncio
from datetime import datetime
import aiohttp
from ..common.const import (
LOGGER,
OAUTH_ACCESS_TOKEN,
OAUTH_DEVICE_LOGIN,
DeviceFlowError,
HttpMethod,
)
from ..common.exceptions import AIOGitHubAPIException
from ..objects.login.device import AIOGitHubAPILoginDevice
from ..objects.login.oauth import AIOGitHubAPILoginOauth
from .helpers import async_call_api
HEADERS = {"Accept": "application/json"}
class AIOGitHubAPIDeviceLogin:
_close_session = False
def __init__(
self,
client_id: str,
scope: str = "",
session: aiohttp.ClientSession = None,
):
"""
Initialises a GitHub API OAuth device flow.
param | required | description
-- | -- | --
`client_id` | True | The client ID of your OAuth app.
`scope` | False | [Scope(s)](https://docs.github.com/en/developers/apps/scopes-for-oauth-apps) that will be requested.
`session` | False | `aiohttp.ClientSession` to be used by this package.
"""
self.client_id = client_id
self.scope = scope
self._interval = 5
self._expires_in = None
self._expires = None
self._device_code = None
if session is None:
self.session = aiohttp.ClientSession()
self._close_session = True
else:
self.session = session
async def __aenter__(self) -> "AIOGitHubAPIDeviceLogin":
"""Async enter."""
return self
async def __aexit__(self, *exc_info) -> None:
"""Async exit."""
await self._close()
async def async_register_device(self) -> AIOGitHubAPILoginDevice:
"""Register the device and return a object that contains the user code for authorization."""
params = {"client_id": self.client_id, "scope": self.scope}
response = await async_call_api(
session=self.session,
method=HttpMethod.POST,
url=OAUTH_DEVICE_LOGIN,
params=params,
headers=HEADERS,
)
device = AIOGitHubAPILoginDevice(response.data)
self._device_code = device.device_code
self._interval = device.interval
self._expires = datetime.timestamp(datetime.now()) + device.expires_in
return device
async def async_device_activation(self) -> AIOGitHubAPILoginOauth:
"""Wait for the user to enter the code and activate the device."""
_activation = None
while _activation is None:
if self._expires is None or self._device_code is None:
await asyncio.sleep(self._interval)
params = {
"client_id": self.client_id,
"device_code": self._device_code,
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
}
if self._expires < datetime.timestamp(datetime.now()):
raise AIOGitHubAPIException("User took too long to enter key")
try:
response = await async_call_api(
session=self.session,
method=HttpMethod.POST,
url=OAUTH_ACCESS_TOKEN,
params=params,
headers=HEADERS,
)
if response.data.get("error"):
if response.data["error"] == DeviceFlowError.AUTHORIZATION_PENDING:
LOGGER.debug(response.data["error_description"])
await asyncio.sleep(self._interval)
else:
raise AIOGitHubAPIException(response.data["error_description"])
else:
_activation = AIOGitHubAPILoginOauth(response.data)
break
except AIOGitHubAPIException as exception:
raise AIOGitHubAPIException(exception) from exception
return _activation
async def _close(self) -> None:
"""Close open client session."""
if self.session and self._close_session:
await self.session.close()
Classes
class AIOGitHubAPIDeviceLogin (client_id: str, scope: str = '', session: aiohttp.ClientSession = None)
-
Initialises a GitHub API OAuth device flow.
param required description client_id
True The client ID of your OAuth app. scope
False Scope(s) that will be requested. session
False aiohttp.ClientSession
to be used by this package.Expand source code
class AIOGitHubAPIDeviceLogin: _close_session = False def __init__( self, client_id: str, scope: str = "", session: aiohttp.ClientSession = None, ): """ Initialises a GitHub API OAuth device flow. param | required | description -- | -- | -- `client_id` | True | The client ID of your OAuth app. `scope` | False | [Scope(s)](https://docs.github.com/en/developers/apps/scopes-for-oauth-apps) that will be requested. `session` | False | `aiohttp.ClientSession` to be used by this package. """ self.client_id = client_id self.scope = scope self._interval = 5 self._expires_in = None self._expires = None self._device_code = None if session is None: self.session = aiohttp.ClientSession() self._close_session = True else: self.session = session async def __aenter__(self) -> "AIOGitHubAPIDeviceLogin": """Async enter.""" return self async def __aexit__(self, *exc_info) -> None: """Async exit.""" await self._close() async def async_register_device(self) -> AIOGitHubAPILoginDevice: """Register the device and return a object that contains the user code for authorization.""" params = {"client_id": self.client_id, "scope": self.scope} response = await async_call_api( session=self.session, method=HttpMethod.POST, url=OAUTH_DEVICE_LOGIN, params=params, headers=HEADERS, ) device = AIOGitHubAPILoginDevice(response.data) self._device_code = device.device_code self._interval = device.interval self._expires = datetime.timestamp(datetime.now()) + device.expires_in return device async def async_device_activation(self) -> AIOGitHubAPILoginOauth: """Wait for the user to enter the code and activate the device.""" _activation = None while _activation is None: if self._expires is None or self._device_code is None: await asyncio.sleep(self._interval) params = { "client_id": self.client_id, "device_code": self._device_code, "grant_type": "urn:ietf:params:oauth:grant-type:device_code", } if self._expires < datetime.timestamp(datetime.now()): raise AIOGitHubAPIException("User took too long to enter key") try: response = await async_call_api( session=self.session, method=HttpMethod.POST, url=OAUTH_ACCESS_TOKEN, params=params, headers=HEADERS, ) if response.data.get("error"): if response.data["error"] == DeviceFlowError.AUTHORIZATION_PENDING: LOGGER.debug(response.data["error_description"]) await asyncio.sleep(self._interval) else: raise AIOGitHubAPIException(response.data["error_description"]) else: _activation = AIOGitHubAPILoginOauth(response.data) break except AIOGitHubAPIException as exception: raise AIOGitHubAPIException(exception) from exception return _activation async def _close(self) -> None: """Close open client session.""" if self.session and self._close_session: await self.session.close()
Subclasses
Methods
async def async_device_activation(self) ‑> AIOGitHubAPILoginOauth
-
Wait for the user to enter the code and activate the device.
Expand source code
async def async_device_activation(self) -> AIOGitHubAPILoginOauth: """Wait for the user to enter the code and activate the device.""" _activation = None while _activation is None: if self._expires is None or self._device_code is None: await asyncio.sleep(self._interval) params = { "client_id": self.client_id, "device_code": self._device_code, "grant_type": "urn:ietf:params:oauth:grant-type:device_code", } if self._expires < datetime.timestamp(datetime.now()): raise AIOGitHubAPIException("User took too long to enter key") try: response = await async_call_api( session=self.session, method=HttpMethod.POST, url=OAUTH_ACCESS_TOKEN, params=params, headers=HEADERS, ) if response.data.get("error"): if response.data["error"] == DeviceFlowError.AUTHORIZATION_PENDING: LOGGER.debug(response.data["error_description"]) await asyncio.sleep(self._interval) else: raise AIOGitHubAPIException(response.data["error_description"]) else: _activation = AIOGitHubAPILoginOauth(response.data) break except AIOGitHubAPIException as exception: raise AIOGitHubAPIException(exception) from exception return _activation
async def async_register_device(self) ‑> AIOGitHubAPILoginDevice
-
Register the device and return a object that contains the user code for authorization.
Expand source code
async def async_register_device(self) -> AIOGitHubAPILoginDevice: """Register the device and return a object that contains the user code for authorization.""" params = {"client_id": self.client_id, "scope": self.scope} response = await async_call_api( session=self.session, method=HttpMethod.POST, url=OAUTH_DEVICE_LOGIN, params=params, headers=HEADERS, ) device = AIOGitHubAPILoginDevice(response.data) self._device_code = device.device_code self._interval = device.interval self._expires = datetime.timestamp(datetime.now()) + device.expires_in return device