Hoping to get some help on making calls to the Coinbase Pro API.
I created a key, noted my phasephrase, key and secret, and am running the below Python script. The response I get is "invalid signature".
On the CBPro documentation site, when I try running it with my credentials on this page, I get a "Sorry, you couldn't be authenticated with those credentails" message.
I've seen some sources that encode to base64 and have success, and others that don't, but neither works for me. What am I don't wrong?
Code:
import requests
import time
import base64
import json
url = "https://api.exchange.coinbase.com/accounts/account_id/transfers?limit=100"
key = "key"
secret = "secret"
passphrase = "pass"
timestamp = str(time.time())
headers = {
"Accept": "application/json",
"cb-access-key": key,
"cb-access-passphrase": passphrase,
"cb-access-sign": encodedData,
"cb-access-timestamp": timestamp
}
response = requests.request("GET", url, headers=headers)
print(response.text)
Signing a request is probably the worst part of the coinbase api.
Here is the documentation for it. some things to note:
signature is only good for 30 seconds so you have to hurry and copy / paste the encoded data and timestamp into the docs form.
the timestamp has to be the same value you use in the encodedData.
the non-pro api signs a little different so make sure you're on the right set of docs. This one I don't think works for pro.
If you're still having trouble authenticating your requests to coinbase here's an example of what i'm using. You might have to change a few things but does the job.
What you're looking for in this example are the HMACAuth & CoinbaseSession classes for your particular need.
# -*- coding: UTF-8 -*-
from base64 import b64encode, b64decode
from collections import namedtuple
from datetime import datetime, timezone
from hashlib import sha256
from hmac import HMAC
from json import loads, JSONDecodeError
from types import SimpleNamespace
from typing import Union, Generator
from requests import Session
from requests.adapters import HTTPAdapter
from requests.auth import AuthBase
from requests.exceptions import HTTPError
from requests.models import PreparedRequest
from requests.utils import to_native_string
from urllib3.util.retry import Retry
EXCHANGE: str = r"https://api.exchange.coinbase.com"
ACCOUNT = namedtuple(
"ACCOUNT",
(
"id",
"currency",
"balance",
"hold",
"available",
"profile_id",
"trading_enabled",
)
)
PRODUCT = namedtuple(
"PRODUCT",
(
"id",
"base_currency",
"quote_currency",
"base_min_size",
"base_max_size",
"quote_increment",
"base_increment",
"display_name",
"min_market_funds",
"max_market_funds",
"margin_enabled",
"fx_stablecoin",
"max_slippage_percentage",
"post_only",
"limit_only",
"cancel_only",
"trading_disabled",
"status",
"status_message",
"auction_mode",
)
)
def encode(value: Union[str, bytes]) -> bytes:
"""Encode the string `value` with UTF-8."""
if isinstance(value, str) is True:
value = value.encode("UTF-8")
return value
def decode(value: Union[bytes, str]) -> str:
"""Decode the bytes-like object `value` with UTF-8."""
if isinstance(value, bytes) is True:
value = value.decode("UTF-8")
return value
def req_time():
"""POSIX timestamp as float. Number of seconds since Unix Epoch in UTC."""
utc = get_utc()
return utc.timestamp()
def get_utc() -> datetime:
"""Construct a datetime object with UTC time zone info."""
return datetime.now(timezone.utc)
class TimeoutHTTPAdapter(HTTPAdapter):
"""Custom HTTP adapter with timeout capability."""
def __init__(self, *args, **kwargs):
self._timeout = kwargs.pop("timeout")
super(TimeoutHTTPAdapter, self).__init__(*args, **kwargs)
def send(self, request, **kwargs):
kwargs.update({"timeout": self._timeout})
return super(TimeoutHTTPAdapter, self).send(request, **kwargs)
class CoinbaseSession(Session):
"""Coinbase Session handle."""
_headers: dict = {
"Accept": "application/json",
"Content-Type": "application/json",
"Accept-Charset": "utf-8",
}
#staticmethod
def http_adapter(retries: int = 3, backoff: int = 1, timeout: int = 30):
return TimeoutHTTPAdapter(
max_retries=Retry(total=retries, backoff_factor=backoff),
timeout=timeout
)
def __init__(self):
super(CoinbaseSession, self).__init__()
self.headers.update(self._headers)
self.auth = HMACAuth()
self.mount("https://", self.http_adapter())
self.mount("http://", self.http_adapter())
class HMACAuth(AuthBase):
"""Requests signing handle."""
#staticmethod
def __pre_hash(timestamp: Union[str, int, float], request: PreparedRequest) -> bytes:
"""
Create the pre-hash string by concatenating the timestamp with
the request method, path_url and body if exists.
"""
message = f"{timestamp}{request.method.upper()}{request.path_url}"
body = request.body
if body is not None:
message = f"{message}{decode(body)}"
return encode(message)
#staticmethod
def __sign(message: bytes) -> bytes:
"""Create a sha256 HMAC and sign the required `message`."""
key = b64decode(encode(API.SECRET)) # be careful were you keep this!
hmac = HMAC(key=key, msg=message, digestmod=sha256).digest()
return b64encode(hmac)
def __call__(self, request: PreparedRequest):
timestamp = req_time()
message = self.__pre_hash(timestamp, request)
cb_access_sign = self.__sign(message)
request.headers.update(
{
to_native_string('CB-ACCESS-KEY'): API.KEY, # be careful where you keep this!
to_native_string('CB-ACCESS-SIGN'): cb_access_sign,
to_native_string('CB-ACCESS-TIMESTAMP'): str(timestamp),
to_native_string('CB-ACCESS-PASSPHRASE'): API.PASSPHRASE, # be careful where you keep this!
}
)
return request
class CoinbaseAPI(object):
"""Coinbase API handle."""
def __init__(self):
self._session = CoinbaseSession()
def request(self, **kwargs):
"""
Send HTTP requests to the Coinbase API.
Raises HTTPError if response is not 200.
"""
print(f"DEBUG: Requesting resource (url = {kwargs.get('url')})")
try:
results = self.__request(**kwargs)
except HTTPError as http_error:
print(f"ERROR: Resource not found!", f"Cause: {http_error}")
else:
print(f"DEBUG: Resource found (url = {kwargs.get('url')})")
return results
def __request(self, **kwargs):
"""
Send HTTP requests to the Coinbase API.
Raises HTTPError if response is not 200.
"""
method = getattr(self._session, kwargs.pop("method"))
response = method(**kwargs)
if response.status_code != 200:
response.raise_for_status()
else:
try:
results = loads(response.text)
except JSONDecodeError as json_error:
print("WARNING: Decoding JSON object failed!", f"Cause: {json_error}")
kwargs.update({"method": method.__name__})
return self.__request(**kwargs)
else:
return results
class Endpoints(object):
"""Coinbase server endpoints."""
_server = None
#staticmethod
def join(*path, **params) -> str:
"""
Construct the resource url by appending all `path`
items to base url and join `params` if any.
"""
url = "/".join(path)
if len(params) > 0:
params = [f"{key}={value}" for key, value in params.items()]
url = f"{url}?{'&'.join(params)}"
return url
class ExchangeEndpoints(Endpoints):
"""Coinbase exchange server endpoints."""
_server = EXCHANGE
def __init__(self):
self.products = self.join(self._server, "products")
self.accounts = self.join(self._server, "accounts")
class Exchange(CoinbaseAPI):
"""Coinbase exchange API client handle."""
_endpoints = ExchangeEndpoints()
def __init__(self):
super(Exchange, self).__init__()
def get_accounts(self) -> Generator:
"""Get a list of trading accounts from the profile of the API key."""
response = self._accounts()
for item in response:
yield ACCOUNT(**item)
def get_account(self, account_id: str) -> ACCOUNT:
"""
Information for a single account. Use this endpoint when you know the account_id.
API key must belong to the same profile as the account.
"""
response = self._accounts(account_id)
return ACCOUNT(**response)
def get_products(self, query: str = None) -> Generator:
"""
Gets a list of available currency pairs for trading.
:param query: `type` query parameter (unknown).
"""
response = self._products(type=query)
for item in response:
yield PRODUCT(**item)
def get_product(self, product_id: str) -> PRODUCT:
"""
Get information on a single product.
:param product_id: The `id` string of the product/currency pair (ex: BTC-USD).
"""
result = self._products(product_id)
return PRODUCT(**result)
def _accounts(self, *path, **params):
"""Access resources from the `accounts` endpoint of the exchange API."""
params = self.clean_params(**params)
_url = self._endpoints.join(
self._endpoints.accounts,
*path,
**params
)
return self.request(method="get", url=_url)
def _products(self, *path, **params):
"""Access resources from the `products` endpoint of the exchange API."""
params = self.clean_params(**params)
_url = self._endpoints.join(
self._endpoints.products,
*path,
**params
)
return self.request(method="get", url=_url)
def clean_params(self, **params) -> dict:
"""Clean `params` by removing None values."""
temp = dict()
for key, value in params.items():
if value is None:
continue
if isinstance(value, dict) is True:
value = self.clean_params(**value)
temp.update({key: value})
return temp
if __name__ == '__main__':
# we're using PBKDF2HMAC (with symmetrically derived encryption key)
# not included in this example
key_vault = KeyVault() # custom class for encrypting and storing secrets to keyring
key_vault.cypher.password(value="your_password", salt="salty_password")
api_credentials = loads(
key_vault.get_password("coinbase", "pro-coinbase-api")
)
# accounts:
ADA: str = "your_crypto_account_id" # ex: 8b9806a4-7395-11ec-9b1a-f02f74d9105d
API = SimpleNamespace(
NAME="pro-coinbase-api",
VERSION="2021-08-27",
KEY=api_credentials.get("key"),
PASSPHRASE=api_credentials.get("passphrase"),
SECRET=api_credentials.get("secret"),
) # not the best example but does the job as long as you don't invite hackers in your PC :)
api = Exchange()
accounts = api.get_accounts()
for account in accounts:
print(account)
account = api.get_account(account_id=ADA)
print(account)
products = api.get_products()
for product in products:
print(product)
product = api.get_product("ATOM-EUR")
print(product)
Related
I have created a package which uploads a data to some storage using Azure AD access token, now I want to write test cases for the code, as I'm not aware of writing test cases have tried few. Can anyone help here, below is the code for my package.
__init__.py file
import json
import requests
import sys
from data import Data
import datetime
from singleton import singleton
#singleton
class CertifiedLogProvider:
def __init__(self, client_id, client_secret):
self.client_id=client_id
self.client_secret= client_secret
self.grant_type="client_credentials"
self.resource="*****"
self.url="https://login.microsoftonline.com/azureford.onmicrosoft.com/oauth2/token"
self.api_url="http://example.com"
self.get_access_token()
def get_access_token(self)-> None:
data={'client_id':self.client_id,'client_secret':self.client_secret,
'grant_type':self.grant_type,'resource':self.resource}
response = requests.post(self.url, data=data)
if response.status_code == 200:
self.token_dict=response.content
self.access_token = response.json()["access_token"]
else:
print(f"An Error occurred: \n {response.text}")
def is_expired(self) -> bool:
token_dict=json.loads(self.token_dict.decode('utf-8'))
if int(datetime.datetime.now().timestamp()) > int(token_dict['expires_on']):
return True
else:
return False
def send_clp_data(self,payload:dict):
obj=Data()
data=obj.data
data['event_metric_body']=payload
if self.is_expired() is True:
self.get_access_token()
headers={"Authorization": "Bearer {}".format(self.access_token),
"Content-Type": "application/json",}
response = requests.post(self.api_url,data=json.dumps(data), headers=headers)
if response.status_code == 201:
print('Data uploaded successfully')
else:
print(f"An Error occurred: \n {response.text}")
singleton.py
def singleton(class_):
instances = {}
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return getinstance
data.py
Contains data which is static
test.py
import json
import unittest
from unittest import TestCase
from unittest.mock import patch
import requests
from unittest.mock import MagicMock
from __init__ import CertifiedLogProvider
import pytest
class MyTestCase(TestCase):
def test_can_construct_clp_instance(self):
object= CertifiedLogProvider(1,2)
#patch('requests.post')
def test_send_clp_data(self, mock_post):
info={"test1":"value1", "test2": "value2"}
response = requests.post("www.clp_api.com", data=json.dumps(info), headers={'Content-Type': 'application/json'})
mock_post.assert_called_with("www.clp_api.com", data=json.dumps(info), headers={'Content-Type': 'application/json'})
if __name__ == '__main__':
unittest.main()
How can we test boolean method and method containing requests?
Testing is_expired() becomes easier if you create a Token class:
class Token:
def __init__(self, token_dict: dict) -> None:
self.token_dict = token_dict
def __str__(self) -> str:
return self.token_dict['access_token']
def is_expired(self, now:DateTime=None) -> bool:
if now is None:
now = datetime.datetime.now()
return int(now.timestamp()) > int(self.token_dict['expires_on'])
#singleton
class CertifiedLogProvider:
def __init__(self, client_id, client_secret):
self.client_id=client_id
self.client_secret= client_secret
self.grant_type="client_credentials"
self.resource="*****"
self.url="https://login.microsoftonline.com/azureford.onmicrosoft.com/oauth2/token"
self.api_url="http://example.com"
self.access_token = None
def get_access_token(self) -> Token:
if self.access_token is None or self.access_token.is_expired():
self.access_token = self.fetch_access_token()
return self.access_token
def fetch_access_token(self) -> Token:
data={
'client_id': self.client_id,
'client_secret': self.client_secret,
'grant_type': self.grant_type,
'resource': self.resource,
}
response = requests.post(self.url, data=data)
if response.status_code != 200:
raise Exception(f"An Error occurred: \n {response.text}")
return Token(response.json())
def send_clp_data(self, payload: dict):
obj=Data()
data=obj.data
data['event_metric_body'] = payload
headers={
"Authorization": f"Bearer {self.get_access_token()}",
"Content-Type": "application/json",
}
response = requests.post(self.api_url, data=json.dumps(data), headers=headers)
if response.status_code != 201:
raise Exception(f"An Error occurred: \n {response.text}")
print('Data uploaded successfully')
test_token.py
from datetime import datetime
import unittest
from certified_log_provider import Token
class TestToken(unittest.TestCase):
def setUp(self) -> None:
self.token = Token({ 'expires_on': datetime.strptime("2022-02-18", "%Y-%m-%d").timestamp() })
def test_token_is_not_expired_day_before(self):
self.assertFalse(self.token.is_expired(datetime.strptime("2022-02-17", "%Y-%m-%d")))
def test_token_is_expired_day_after(self):
self.assertTrue(self.token.is_expired(datetime.strptime("2022-02-19", "%Y-%m-%d")))
I tried to connect to the Riot api to access the summoner info. I wrote 3 files:
"RiotConsts": a file setting some constants such as "URL", "api_version", "region"
"RiotAPI": the main functions
"riot_main": to call the info I want.
I am receiving an error: "RiotAPI' object has no attribute '_request'".
class RiotAPI(object):
def __init__(self,api_key,region=Consts.REGIONS['europe_nordic_and_east']):
self.api_key = api_key
self.region = region
def request(self, api_key, params={}):
args = {'api_key': self.api_key}
for k,v in params.items():
if k not in args:
args[k] = v
response = requests.get(
Consts.URL['base'].format(
proxy = self.region,
region = self.region,
url = api_url
),
params=args
)
print (response.url)
return response.json()
def get_summoner_by_name(self, name):
api_url = Consts.URL['summoner_by_name'].format(
version=Consts.API_VERSIONS['summoner'],
summonerName=name
)
return self._request(api_url)
I expect to receive the summoner info but I got:
'RiotAPI' object has no attribute '_request'
Simpler code to connect Riot API and to have summoner infos :
class RiotApi(object):
def __init__(self, api_key: str, region="euw1"):
self.__RIOT_API_KEY = api_key
self.__HEADER = {'X-Riot-Token': self.__RIOT_API_KEY}
self.__REGION = region
self.__BASE_URL = ".api.riotgames.com/lol/"
self.__API_URL_SUMMONER_V4 = "https://" + self.__REGION + self.__BASE_URL + "summoner/v4/summoners/"
def get_summoner_by_name(self, summoner_name: str) -> dict:
"""Summoner Infos and Ids
#param summoner_name: LoL summoner name
#return json object of infos and ids
"""
url = self.__API_URL_SUMMONER_V4 + "by-name/" + summoner_name
request = requests.get(url, headers=self.__HEADER)
return request.json()
As mentioned in comment, object has no attribute error is caused by invocation of undefined attribute (or method) -- in this case _request() method. So, assuming the rest of the code are correct, you can try this code:
# class RiotAPI(object):
class RiotAPI:
def __init__(self,api_key,region=Consts.REGIONS['europe_nordic_and_east']):
self.api_key = api_key
self.region = region
# def request(self, api_key, params={}):
def request(self, api_url, params={}):
args = {'api_key': self.api_key}
for k,v in params.items():
if k not in args:
args[k] = v
response = requests.get(
Consts.URL['base'].format(
proxy = self.region,
region = self.region,
url = api_url
),
params=args
)
print (response.url)
return response.json()
def get_summoner_by_name(self, name):
api_url = Consts.URL['summoner_by_name'].format(
version=Consts.API_VERSIONS['summoner'],
summonerName=name
)
#return self._request(api_url)
return self.request(api_url)
I am trying to pass my session object from one class to another. But I am not sure whats happening.
class CreateSession:
def __init__(self, user, pwd, url="http://url_to_hit"):
self.url = url
self.user = user
self.pwd = pwd
def get_session(self):
sess = requests.Session()
r = sess.get(self.url + "/", auth=(self.user, self.pwd))
print(r.content)
return sess
class TestGet(CreateSession):
def get_response(self):
s = self.get_session()
print(s)
data = s.get(self.url + '/some-get')
print(data.status_code)
print(data)
if __name__ == "__main__":
TestGet(user='user', pwd='pwd').get_response()
I am getting 401 for get_response(). Not able to understand this.
What's a 401?
The response you're getting means that you're unauthorised to access the resource.
A session is used in order to persist headers and other prerequisites throughout requests, why are you creating the session every time rather than storing it in a variable?
As is, the session should work the only issue is that you're trying to call a resource that you don't have access to. - You're not passing the url parameter either in the initialisation.
Example of how you can effectively use Session:
from requests import Session
from requests.exceptions import HTTPError
class TestGet:
__session = None
__username = None
__password = None
def __init__(self, username, password):
self.__username = username
self.__password = password
#property
def session(self):
if self.__session is None:
self.__session = Session()
self.__session.auth = (self.__user, self.__pwd)
return self.__session
#session.setter
def session(self, value):
raise AttributeError('Setting \'session\' attribute is prohibited.')
def get_response(self, url):
try:
response = self.session.get(url)
# raises if the status code is an error - 4xx, 5xx
response.raise_for_status()
return response
except HTTPError as e:
# you received an http error .. handle it here (e contains the request and response)
pass
test_get = TestGet('my_user', 'my_pass')
first_response = test_get.get_response('http://your-website-with-basic-auth.com')
second_response = test_get.get_response('http://another-url.com')
my_session = test_get.session
my_session.get('http://url.com')
Hello Stackoveflow members
I am making a Google Plus user authentication using Oauth and at the same time I need to fetch user profile, pics and drive information as well. The best way to do this is to use Oauth. So I am using flask_googlelogin.
I am trying to use the example.py of this library but What I found that the API works well and my Application pages comes up with information and Cancel and Accept button. But when I push accept button, I get the flask error
TypeError: 'instancemethod' object has no attribute 'getitem'
Now please have a look at the example.py code and flask_googlelogin.py code
here
import json
from flask import Flask, url_for, redirect, session
from flask_login import (UserMixin, login_required, login_user, logout_user,
current_user)
from flask_googlelogin import GoogleLogin
users = {}
app = Flask(__name__)
app.config.update(
SECRET_KEY='<secret_key>',
GOOGLE_LOGIN_CLIENT_ID='<client_id>',
GOOGLE_LOGIN_CLIENT_SECRET='<client_secret>',
GOOGLE_LOGIN_REDIRECT_URI='<redirection_url>')
googlelogin = GoogleLogin(app)
class User(UserMixin):
def __init__(self, userinfo):
self.id = userinfo['id']
self.name = userinfo['name']
self.picture = userinfo.get('picture')
#googlelogin.user_loader
def get_user(userid):
return users.get(userid)
#app.route('/')
def index():
return """
<p><a href="%s">Login</p>
<p><a href="%s">Login with extra params</p>
<p><a href="%s">Login with extra scope</p>
""" % (
googlelogin.login_url(approval_prompt='force'),
googlelogin.login_url(approval_prompt='force',
params=dict(extra='large-fries')),
googlelogin.login_url(
approval_prompt='force',
scopes=['https://www.googleapis.com/auth/drive'],
access_type='offline',
),
)
#app.route('/profile')
#login_required
def profile():
return """
<p>Hello, %s</p>
<p><img src="%s" width="100" height="100"></p>
<p>Token: %r</p>
<p>Extra: %r</p>
<p>Logout</p>
""" % (current_user.name, current_user.picture, session.get('token'),
session.get('extra'))
#app.route('/oauth2callback')
#googlelogin.oauth2callback
def login(token, userinfo, **params):
user = users[userinfo['id']] = User(userinfo)
login_user(user)
session['token'] = json.dumps(token)
session['extra'] = params.get('extra')
return redirect(params.get('next', url_for('.profile')))
#app.route('/logout')
def logout():
logout_user()
session.clear()
return """
<p>Logged out</p>
<p>Return to /</p>
"""
app.run(debug=True)
and flask_googlelogin.py code here
"""
Flask-GoogleLogin
"""
from base64 import (urlsafe_b64encode as b64encode,
urlsafe_b64decode as b64decode)
from urllib import urlencode
from urlparse import parse_qsl
from functools import wraps
from flask import request, redirect, abort, current_app, url_for
from flask_login import LoginManager, make_secure_token
import requests
GOOGLE_OAUTH2_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth'
GOOGLE_OAUTH2_TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'
GOOGLE_OAUTH2_USERINFO_URL = 'https://www.googleapis.com/oauth2/v1/userinfo'
USERINFO_PROFILE_SCOPE = 'https://www.googleapis.com/auth/userinfo.profile'
class GoogleLogin(object):
"""
Main extension class
"""
def __init__(self, app=None, login_manager=None):
if login_manager:
self.login_manager = login_manager
else:
self.login_manager = LoginManager()
if app:
self._app = app
self.init_app(app)
def init_app(self, app, add_context_processor=True, login_manager=None):
"""
Initialize with app configuration. Existing
`flask_login.LoginManager` instance can be passed.
"""
if login_manager:
self.login_manager = login_manager
else:
self.login_manager = LoginManager()
# Check if login manager has been init
if not hasattr(app, 'login_manager'):
self.login_manager.init_app(
app,
add_context_processor=add_context_processor)
# Clear flashed messages since we redirect to auth immediately
self.login_manager.login_message = None
self.login_manager.needs_refresh_message = None
# Set default unauthorized callback
self.login_manager.unauthorized_handler(self.unauthorized_callback)
#property
def app(self):
return getattr(self, '_app', current_app)
#property
def scopes(self):
return self.app.config.get('GOOGLE_LOGIN_SCOPES', '')
#property
def client_id(self):
return self.app.config['GOOGLE_LOGIN_CLIENT_ID']
#property
def client_secret(self):
return self.app.config['GOOGLE_LOGIN_CLIENT_SECRET']
#property
def redirect_uri(self):
return self.app.config.get('GOOGLE_LOGIN_REDIRECT_URI')
#property
def redirect_scheme(self):
return self.app.config.get('GOOGLE_LOGIN_REDIRECT_SCHEME', 'http')
def sign_params(self, params):
return b64encode(urlencode(dict(sig=make_secure_token(**params),
**params)))
def parse_state(self, state):
return dict(parse_qsl(b64decode(str(state))))
def login_url(self, params=None, **kwargs):
"""
Return login url with params encoded in state
Available Google auth server params:
response_type: code, token
prompt: none, select_account, consent
approval_prompt: force, auto
access_type: online, offline
scopes: string (separated with commas) or list
redirect_uri: string
login_hint: string
"""
kwargs.setdefault('response_type', 'code')
kwargs.setdefault('access_type', 'online')
if 'prompt' not in kwargs:
kwargs.setdefault('approval_prompt', 'auto')
scopes = kwargs.pop('scopes', self.scopes.split(','))
if USERINFO_PROFILE_SCOPE not in scopes:
scopes.append(USERINFO_PROFILE_SCOPE)
redirect_uri = kwargs.pop('redirect_uri', self.redirect_uri)
state = self.sign_params(params or {})
return GOOGLE_OAUTH2_AUTH_URL + '?' + urlencode(
dict(client_id=self.client_id,
scope=' '.join(scopes),
redirect_uri=redirect_uri,
state=state,
**kwargs))
def unauthorized_callback(self):
"""
Redirect to login url with next param set as request.url
"""
return redirect(self.login_url(params=dict(next=request.url)))
def exchange_code(self, code, redirect_uri):
"""
Exchanges code for token/s
"""
token = requests.post(GOOGLE_OAUTH2_TOKEN_URL, data=dict(
code=code,
redirect_uri=redirect_uri,
grant_type='authorization_code',
client_id=self.client_id,
client_secret=self.client_secret,
)).json
if not token: # or token.get('error'):
abort(400)
return token
def get_userinfo(self, access_token):
userinfo = requests.get(GOOGLE_OAUTH2_USERINFO_URL, params=dict(
access_token=access_token,
)).json
if not userinfo: # or userinfo.get('error'):
abort(400)
return userinfo
def get_access_token(self, refresh_token):
"""
Use a refresh token to obtain a new access token
"""
token = requests.post(GOOGLE_OAUTH2_TOKEN_URL, data=dict(
refresh_token=refresh_token,
grant_type='refresh_token',
client_id=self.client_id,
client_secret=self.client_secret,
)).json
if not token: # or token.get('error'):
return
return token
def oauth2callback(self, view_func):
"""
Decorator for OAuth2 callback. Calls `GoogleLogin.login` then
passes results to `view_func`.
"""
#wraps(view_func)
def decorated(*args, **kwargs):
params = {}
# Check sig
if 'state' in request.args:
params.update(**self.parse_state(request.args.get('state')))
if params.pop('sig', None) != make_secure_token(**params):
return self.login_manager.unauthorized()
code = request.args.get('code')
# Web server flow
if code:
token = self.exchange_code(
code,
url_for(
request.endpoint,
_external=True,
_scheme=self.redirect_scheme,
),
)
#received = get_access_token(token['access_token'])
userinfo = self.get_userinfo(token['access_token'])
params.update(token=token, userinfo=userinfo)
# Browser flow
else:
if params:
params.update(dict(request.args.items()))
else:
return '''
<script>
window.onload = function() {
location.href = '?' + window.location.hash.substr(1);
};
</script>
'''
return view_func(**params)
return decorated
def user_loader(self, func):
"""
Shortcut for `login_manager`'s `flask_login.LoginManager.user_loader`
"""
self.login_manager.user_loader(func)
Please Note down there may be some disturbed code in def oauth2callback and the condition if code the line is
userinfo = self.get_userinfo(token['access_token'])
Here token['access_token'] produces the error names "TypeError: 'instancemethod' object has no attribute 'getitem'"
Please let me know how can I fix it
Looks like a bug or api change.
In exchange_code
token = requests.post(GOOGLE_OAUTH2_TOKEN_URL, data=dict(
code=code,
redirect_uri=redirect_uri,
grant_type='authorization_code',
client_id=self.client_id,
client_secret=self.client_secret,
)).json
token is now the json function. In newer versions of flask_googlelogin this is json().
I was wondering if it is possible to create an upload function to upload picture through my own site to the gravatar site?
Yes, this is possible. See http://en.gravatar.com/site/implement/xmlrpc/ , specifically the grav.saveData or grav.SaveUrl calls.
Yes it's possible!
import base64
from xmlrpclib import ServerProxy, Fault
from hashlib import md5
class GravatarXMLRPC(object):
API_URI = 'https://secure.gravatar.com/xmlrpc?user={0}'
def __init__(self, request, password=''):
self.request = request
self.password = password
self.email = sanitize_email(request.user.email)
self.email_hash = md5_hash(self.email)
self._server = ServerProxy(
self.API_URI.format(self.email_hash))
def saveData(self, image):
""" Save binary image data as a userimage for this account """
params = { 'data': base64_encode(image.read()), 'rating': 0, }
return self._call('saveData', params)
#return self.useUserimage(image)
def _call(self, method, params={}):
""" Call a method from the API, gets 'grav.' prepended to it. """
args = { 'password': self.password, }
args.update(params)
try:
return getattr(self._server, 'grav.' + method, None)(args)
except Fault as error:
error_msg = "Server error: {1} (error code: {0})"
print error_msg.format(error.faultCode, error.faultString)
def base64_encode(obj):
return base64.b64encode(obj)
def sanitize_email(email):
return email.lower().strip()
def md5_hash(string):
return md5(string.encode('utf-8')).hexdigest()
Just call the class in your view :)