""" Khan Academy OAuth backend, docs at: https://github.com/Khan/khan-api/wiki/Khan-Academy-API-Authentication """ from urllib.parse import urlencode from oauthlib.oauth1 import SIGNATURE_HMAC, SIGNATURE_TYPE_QUERY from requests_oauthlib import OAuth1 from .oauth import BaseOAuth1 class BrowserBasedOAuth1(BaseOAuth1): """Browser based mechanism OAuth authentication, fill the needed parameters to communicate properly with authentication service. REQUEST_TOKEN_URL Request token URL (opened in web browser) ACCESS_TOKEN_URL Access token URL """ REQUEST_TOKEN_URL = "" OAUTH_TOKEN_PARAMETER_NAME = "oauth_token" REDIRECT_URI_PARAMETER_NAME = "redirect_uri" ACCESS_TOKEN_URL = "" def auth_url(self): """Return redirect url""" return self.unauthorized_token_request() def get_unauthorized_token(self): return self.strategy.request_data() def unauthorized_token_request(self): """Return request for unauthorized token (first stage)""" params = self.request_token_extra_arguments() params.update(self.get_scope_argument()) key, secret = self.get_key_and_secret() state = self.get_or_create_state() auth = OAuth1( key, secret, callback_uri=self.get_redirect_uri(state), signature_method=SIGNATURE_HMAC, signature_type=SIGNATURE_TYPE_QUERY, decoding=None, ) url = self.REQUEST_TOKEN_URL + "?" + urlencode(params) url, _, _ = auth.client.sign(url) return url def oauth_auth(self, token=None, oauth_verifier=None): key, secret = self.get_key_and_secret() oauth_verifier = oauth_verifier or self.data.get("oauth_verifier") token = token or {} state = self.get_or_create_state() return OAuth1( key, secret, resource_owner_key=token.get("oauth_token"), resource_owner_secret=token.get("oauth_token_secret"), callback_uri=self.get_redirect_uri(state), verifier=oauth_verifier, signature_method=SIGNATURE_HMAC, signature_type=SIGNATURE_TYPE_QUERY, decoding=None, ) class KhanAcademyOAuth1(BrowserBasedOAuth1): """ Class used for autorising with Khan Academy. Flow of Khan Academy is a bit different than most OAuth 1.0 and consinsts of the following steps: 1. Create signed params to attach to the REQUEST_TOKEN_URL 2. Redirect user to the REQUEST_TOKEN_URL that will respond with oauth_secret, oauth_token, oauth_verifier that should be used with ACCESS_TOKEN_URL 3. Go to ACCESS_TOKEN_URL and grab oauth_token_secret. Note that we don't use the AUTHORIZATION_URL. REQUEST_TOKEN_URL requires the following arguments: oauth_consumer_key - Your app's consumer key oauth_nonce - Random 64-bit, unsigned number encoded as an ASCII string in decimal format. The nonce/timestamp pair should always be unique. oauth_version - OAuth version used by your app. Must be "1.0" for now. oauth_signature - String generated using the referenced signature method. oauth_signature_method - Signature algorithm (currently only support "HMAC-SHA1") oauth_timestamp - Integer representing the time the request is sent. The timestamp should be expressed in number of seconds after January 1, 1970 00:00:00 GMT. oauth_callback (optional) - URL to redirect to after request token is received and authorized by the user's chosen identity provider. """ name = "khanacademy-oauth1" ID_KEY = "user_id" REQUEST_TOKEN_URL = "http://www.khanacademy.org/api/auth/request_token" ACCESS_TOKEN_URL = "https://www.khanacademy.org/api/auth/access_token" REDIRECT_URI_PARAMETER_NAME = "oauth_callback" USER_DATA_URL = "https://www.khanacademy.org/api/v1/user" EXTRA_DATA = [("user_id", "user_id")] def get_user_details(self, response): """Return user details from Khan Academy account""" return { "username": response.get("email"), "email": response.get("email"), "fullname": response.get("nickname"), "first_name": "", "last_name": "", "user_id": response.get("user_id"), } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" auth = self.oauth_auth(access_token) url, _, _ = auth.client.sign(self.USER_DATA_URL) return self.get_json(url)