Receiving HttpError 403 When executing custom google endpoints api - google-app-engine

I have created an endpoints api in Google Apps Engine with python. The api requires oAuth2 authentication. The method works perfectly from the api explorer, however I can't seem to figure out why I get an HttpError 403 "Client_id is not allowed" at runtime. I know that the client id is valid because I have been able to use it successfully in api explorer. I know I am exposing my client secret in this post, but I need the help. Once I get it working I will request a new secret and client id.
The code that is calling the API:
from apiclient.discovery import build
from oauth2client.appengine import OAuth2Decorator
import logging
import pprint
import webapp2
decorator = OAuth2Decorator(
client_id='968442935974-happ8rfg8eq059v1p8364bv5mj8tjd4l.apps.googleusercontent.com',
client_secret='q31TNSbsc70xfEwaTjXuVAPI',
scope='https://www.googleapis.com/auth/userinfo.email')
# For for production
api_root = 'https://jamesste-school-app.appspot.com/_ah/api'
api = 'knowledge'
version = 'v1'
discovery_url = '%s/discovery/v1/apis/%s/%s/rest' % (api_root, api, version)
class TestHandler(webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
service = build(api,version,discoveryServiceUrl=discovery_url)
self.response.headers['Content-Type'] = 'application/json'
knowledge_list = service.knowledge_message().list()
knowledge_list.execute(http=decorator.http())
self.response.out.write(pprint.pformat(knowledge_list))
app = webapp2.WSGIApplication([
('/test', TestHandler),
(decorator.callback_path, decorator.callback_handler())],
debug=True)
The API code:
from google.appengine.ext import ndb
from models import Knowledge
from models import KnowledgeType
from models import Resource
from protorpc import message_types
from protorpc import messages
from protorpc import remote
from apiuser import ApiUser
import endpoints
import logging
def get_endpoints_current_user(raise_unauthorized=True):
"""Returns a current user and (optionally) causes an HTTP 401 if no user.
Args:
raise_unauthorized: Boolean; defaults to True. If True, this method
raises an exception which causes an HTTP 401 Unauthorized to be
returned with the request.
Returns:
The signed in user if there is one, else None if there is no signed in
user and raise_unauthorized is False.
"""
current_user = endpoints.get_current_user()
if raise_unauthorized and current_user is None:
raise endpoints.UnauthorizedException('Invalid token.')
return current_user
class KnowledgeMessage(messages.Message):
knowledge_key = messages.StringField(1)
knowledge = messages.StringField(2)
resource_key = messages.StringField(3)
resource_name = messages.StringField(4)
resource_section_key = messages.StringField(5)
resource_section_name = messages.StringField(6)
types = messages.StringField(7, repeated=True)
page = messages.IntegerField(8)
keywords = messages.StringField(9, repeated=True)
user_email = messages.StringField(10)
user_org_key = messages.StringField(11)
class KnowledgeMessageList(messages.Message):
items = messages.MessageField(KnowledgeMessage, 1, repeated=True)
#endpoints.api(name="knowledge", version="v1", description="API for Knowledge management",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=['1-web-apps.apps.googleusercontent.com',
'2-android-apps.apps.googleusercontent.com',
endpoints.API_EXPLORER_CLIENT_ID])
class KnowledgeMessageApi(remote.Service):
#endpoints.method(KnowledgeMessage, KnowledgeMessage, name="knowledge_message.insert", path="knowledge", http_method="POST")
def insert_knowledge(self, request):
authenticated_user = ApiUser()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - authenticated_user.email = " + authenticated_user.email)
PARENT_KEY = authenticated_user.get_org_key()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - authenticated_user.get_org_key()")
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.knowledge = " + request.knowledge)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.resource_key = " + request.resource_key)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.resource_section_key = " + request.resource_section_key)
types_string = ""
for item in request.types:
types_string += item.strip()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.types = " + types_string)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.page = " + str(request.page))
keywords_string = ""
for item in request.keywords:
keywords_string += item.strip()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.keywords = " + keywords_string)
if request.knowledge_key:
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.knowledge_key = " + request.knowledge_key)
# If no key exists, then we can assume it needs to be inserted
# into the datastore. Overwrite any exiting values with what
# is passed into the method
kno=ndb.Key(urlsafe=request.knowledge_key).get()
kno.knowledge = request.knowledge
kno.resource = ndb.Key(urlsafe=request.resource_key)
kno.resource_section = ndb.Key(urlsafe=request.resource_section_key)
kno.types = request.types
kno.page = request.page
kno.keywords = request.keywords
kno.put()
else:
#get resource key and name
Knowledge(parent=PARENT_KEY,
knowledge=request.knowledge,
resource = ndb.Key(urlsafe=request.resource_key),
resource_section = ndb.Key(urlsafe=request.resource_section_key),
types = request.types,
page = request.page,
keywords = request.keywords
).put()
return request
#endpoints.method(message_types.VoidMessage, KnowledgeMessageList, name='knowledge_message.list', path='knowledge_messages', http_method='GET')
def list_knowledge(self, unused_request):
authenticated_user = ApiUser()
if not authenticated_user.is_authenticated:
logging.info("Logging: KnowledgeMessageApi.list_knowledge - user was not authenticated")
raise endpoints.ForbiddenException()
logging.info("finding current user info : " + authenticated_user.email)
PARENT_KEY = authenticated_user.get_org_key()
knos = []
for kno in Knowledge.query(ancestor=PARENT_KEY):
knos.append(KnowledgeMessage(knowledge_key=kno.key.urlsafe(),
knowledge=kno.knowledge,
resource_key=kno.resource.urlsafe(),
resource_name=kno.resource.get().name,
resource_section_key=kno.resource_section.urlsafe(),
resource_section_name=kno.resource_section.get().name,
types=kno.types,
page=kno.page,
keywords=kno.keywords,
user_email=authenticated_user.email,
user_org_key=PARENT_KEY.urlsafe())
)
return KnowledgeMessageList(items=knos)
app = endpoints.api_server([KnowledgeMessageApi])
I'll also include a few images of proofs that I explored prior to sending the question.
enter image description here
I will greatly appreciate any help I receive.

I discovered my mistake after much work:
In my api, I never specified which client_ids were permitted.
#endpoints.api(name="knowledge", version="v1", description="API for Knowledge management",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=['1-web-apps.apps.googleusercontent.com',
'2-android-apps.apps.googleusercontent.com',
endpoints.API_EXPLORER_CLIENT_ID])
class KnowledgeMessageApi(remote.Service):
Apparently the code '1-web-apps.apps.googleusercontent.com' was a placeholder, and I never replaced it with my own. That has been corrected.

Related

Spotipy invalid access token ? scope problem?

Im doing a simple script to update a playlist but I have an error even if everything looks good: `
The script start, I URI works and give me a token, spotify open to asked me the access but after it fail :
HTTP Error for PUT to https://api.spotify.com/v1/playlists/6rkyVmrfPEeWkJm01mbhO1 with
Params: {} returned 401 due to Invalid access token
Traceback (most recent call last):
File "C:\Users\xxxx\AppData\Local\Programs\Python\Python311\Lib\site-packages\spotipy\client.py", line 269, in _internal_call
response.raise_for_status()
File "C:\Users\xxxx\AppData\Local\Programs\Python\Python311\Lib\site-packages\requests\models.py", line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url:
https://api.spotify.com/v1/playlists/6rkyVmrfPEeWkJm01mbhO1
Python code:
def func():
scope = 'playlist-modify-public'
username = 'myusername'
SPOTIPY_CLIENT_ID = 'xxxxxxxxxxxx'
SPOTIPY_CLIENT_SECRET = 'xxxxxxxxx'
token = util.prompt_for_user_token(username,scope,SPOTIPY_CLIENT_ID,SPOTIPY_CLIENT_SECRET,redirect_uri='https://www.dev.com')
print(token)
sp = spotipy.Spotify(auth="token")
id = 'https://open.spotify.com/playlist/6rkyVmrfPEeWkJm01mbhO1'
playlist_name = 'it works'
playlist_description = 'updated by script'
sp.user_playlist_change_details(username, id, name=playlist_name, description=playlist_description)
func()
here another try :
I get :
py spotify_bot.py
HTTP Error for PUT to https://api.spotify.com/v1/playlists/6rkyVmrfPEeWkJm01mbhO1 with Params: {} returned 403 due to Not allowed
An exception occurred
Here the code
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import json
def func():
SCOPE = 'playlist-modify-public'
USER_ID = 'xifeat'
REDIRECT_URI = 'http://localhost:3000/callback'
CLIENT_ID = '6cff431d1dea40f4812460b4721032b0'
CLIENT_SECRET = 'fb0bcffd4b2b41ac84b9b1d7501dbb80'
PLAYLIST_ID = '6rkyVmrfPEeWkJm01mbhO1'
PLAYLIST_NAME = 'test'
DESCRIPTION = 'Updated !'
try:
auth_manager = SpotifyOAuth(
scope=SCOPE,
username=USER_ID,
redirect_uri=REDIRECT_URI,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET)
spotify = spotipy.Spotify(auth_manager=auth_manager)
spotify.user_playlist_change_details(
user=USER_ID,
playlist_id=PLAYLIST_ID,
name=PLAYLIST_NAME,
description=DESCRIPTION)
except:
print("An exception occurred")
func()
`
Yes, it is a Scope problem.
The playlist you are trying to change, is private/not public*. That requires the playlist-modify-private scope.
* On Spotify, all playlists that are not displayed on your profile, are considered private/not public.
You needs to pass correct parameters in here
The playlist_id just id of playlist, it is not full URL.
def user_playlist_change_details(
self,
user,
playlist_id,
name=None,
public=None,
collaborative=None,
description=None,
):
Parameters:
- user - the id of the user
- playlist_id - the id of the playlist
- name - optional name of the playlist
- public - optional is the playlist public
- collaborative - optional is the playlist collaborative
- description - optional description of the playlist
This code will works
import spotipy
from spotipy.oauth2 import SpotifyOAuth
import json
def func():
SCOPE = ['playlist-modify-private','playlist-modify-public']
USER_ID = 'your-user-id'
REDIRECT_URI = 'your-redirect-uri'
CLIENT_ID = 'your-client-id'
CLIENT_SECRET = 'your-client-secret'
PLAYLIST_ID = 'your-play-list'
PLAYLIST_NAME = 'Oh my new playlist name!'
DESCRIPTION = 'This is new description'
try:
auth_manager = SpotifyOAuth(
scope=SCOPE,
username=USER_ID,
redirect_uri=REDIRECT_URI,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET)
spotify = spotipy.Spotify(auth_manager=auth_manager)
spotify.user_playlist_change_details(
user=USER_ID,
playlist_id=PLAYLIST_ID,
name=PLAYLIST_NAME,
description=DESCRIPTION)
except:
print("An exception occurred")
func()
Before change
After change
The scope is ['playlist-modify-private','playlist-modify-public']

'Blueprint' object has no attribute 'config'

I am using flask-mail. But when i call the rest api in front end(react.js) of flask mail i am getting this error
'Blueprint' object has no attribute 'config'
Here is my code for flask mail
from flask import Flask,Blueprint
from flask_mail import Mail, Message
app = Blueprint('app', __name__)
app.register_blueprint(url_prefix='/api/v1/SalesLead')
mail=Mail(app)
app.config['MAIL_SERVER']='smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'myvar30#gmail.com'
app.config['MAIL_PASSWORD'] = '*****'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
mail = Mail(leadHistoryController)
#app.route("/")
def index():
msg = Message('Sheraspace', sender = 'myvar30#gmail.com', recipients = ['jobaer.jhs#gmail.com'])
msg.body = "Hello jh"
mail.send(msg)
return "Sent again"
if __name__ == '__main__':
app.run(debug = True)
Is there any solution for blueprint config? Or can i use the rest api in front end without using blueprint?
Must use flask.current_app instead.
from flask import current_app
def gen_file_name(filename):
"""
If file was exist already, rename it and return a new name
"""
fielname = current_app.config['UPLOAD_FOLDER']
return filename
There is similar question here
blueprint-config

AngularJS + django REST api

I'm attempting to build an api with DRF.
Client is a cordova app backed with AngularJS.
When I try to post some user object using $resource I'm getting a 403 forbidden response from django.
Below is some code which I think is relevant for the issue:
The API Call:
$rootScope.user =
User.get({id: response.id}).$promise.then(angular.noop, function (e) {
if (e.status == 404) { //If not found, register the user.
$rootScope.user = new User();
Object.keys(response).forEach(function (key) {
$rootScope.user[key] = response[key];
});
$rootScope.user.$save(); //Fails here! 403.
}
else
console.log(JSON.stringify(e.msg));
});
The User factory:
.factory('User', function ($resource, serverConstants) {
return $resource(serverConstants.serverUrl + '/users/:id');
})
django view:
# Users
class UserSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.CharField(max_length=100,required=True)
email = serializers.EmailField(required=False,allow_blank=True)
joined = serializers.DateField(required=False,default=datetime.date.today)
class Meta:
model = models.User
fields = ('joined', 'id', 'email')
def get_validation_exclusions(self):
exclusions = super(UserSerializer, self).get_validation_exclusions()
return exclusions + ['owner']
class UserViewSet(viewsets.ModelViewSet):
queryset = models.User.objects.all()
serializer_class = UserSerializer
PS: I've configured angular to use CSRF cookie and django to allow CORS
Thanks in advance!
Your /user/:id endpoint requires authenticated requests.
You need to authenticate your client's requests using one of the methods specified on the previous link.
Given your app runs in a WebView and then has a builtin cookies handling, SessionAuthentication is the more straightforward to implement.
If you want the endpoint to not require authentication, you can set its permission_classes attribute like so:
from rest_framework.permissions import AllowAny
class UserViewSet(viewsets.ModelViewSet):
queryset = models.User.objects.all()
serializer_class = UserSerializer
permission_classes = (AllowAny, )
I guess with DRF you mean the django-rest-framework.
If yes, have a look here:
http://www.django-rest-framework.org/api-guide/authentication/
You can make the view public but using AllowAny.
from rest_framework.permissions import AllowAny
from rest_framework import generics
restapi_permission_classes = (AllowAny,)
class MyListView(generics.ListCreateAPIView):
serializer_class = MyObjectSerializer
permission_classes = restapi_permission_classes
queryset = MyObject.objects.all()
However I'd recommend you to use proper authentication once you are done with testing. I've been using the token authentication.
Have a look at this post for more details:
Django Rest Framework Token Authentication

User is null in the Google Cloud Api

I followed the instructions in this tutorial.
https://developers.google.com/appengine/docs/java/endpoints/getstarted/auth
when i deployed my code. and went to test my app.
with the following url
http://chandru-compute.appspot.com/_ah/api/explorer
My
helloworld.greetings.multiply and
helloworld.greetings.getGreeting works as expected.
But i have issues with the helloworld.greetings.authed method.
The user object is always null.
Here is the code.
package com.google.devrel.samples.helloendpoints;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import javax.inject.Named;
import java.util.ArrayList;
/**
* Defines v1 of a helloworld API, which provides simple "greeting" methods.
*/
#Api(
name = "helloworld",
version = "v1",
clientIds = {com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}
)
public class Greetings {
public static ArrayList<Greeting> greetings = new ArrayList<Greeting>();
static {
greetings.add(new Greeting("hello world!"));
greetings.add(new Greeting("goodbye world!"));
}
public Greeting getGreeting(#Named("id") Integer id) {
return greetings.get(id);
}
#ApiMethod(name = "greetings.multiply", httpMethod = "post")
public Greeting insertGreeting(#Named("times") Integer times, Greeting greeting) {
Greeting response = new Greeting();
StringBuilder responseBuilder = new StringBuilder();
for (int i = 0; i < times; i++) {
responseBuilder.append(greeting.getMessage());
}
response.setMessage(responseBuilder.toString());
return response;
}
#ApiMethod(name = "greetings.authed", path = "greeting/authed")
public Greeting authedGreeting(User user) {
//Greeting response = new Greeting("hello " + user.getEmail());
Greeting response;
if (user == null) {
UserService userService = UserServiceFactory.getUserService();
User user2 = userService.getCurrentUser();
String text = null;
if (user2 != null){
text = user2.getEmail();
}
response = new Greeting("hello world : Email2" + text );
} else {
response = new Greeting("hello world : Email " + user.getEmail() );
}
return response;
}
}
I had same problem, it helped for me to add
scopes = {"https://www.googleapis.com/auth/userinfo.email"}
into my Greetings #Api annotation. So the whole final #Apilook like
#Api(
name = "helloworld",
version = "v1",
clientIds = { com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID },
scopes = {"https://www.googleapis.com/auth/userinfo.email"}
)
Then deploy, reload Api Explorer page and also turn on "Authorize requests using OAuth 2.0" option with same scope.
I am getting the same problem. And if you throw an OAuthRequestException Exception and test the service via the API Explorer console, you will get a message saying This method requires you to be authenticated. You may need to activate the toggle above to authorize your request using OAuth 2.0. When you try to enable the OAuth 2.0 toggle it requests in a new window to Select OAuth 2.0 scopes, and I have not been able to find which scopes are needed or figure out how I can test a cloud end-point service with authorization from the API Explorer console.
First of all, in the API explorer, you need to authenticate the request with OAuth using the Authorize requests using OAuth 2.0 toggle in the user interface.
If the user is still null check that among the client ids there is the ID for the API explorer
#Api(
name = "myAPIName",
version = "v1",
clientIds = { com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID }
)
This is the only thing that is needed to obtain a not null User argument.

AppEngine Python - how to deal with cached object values

Newbie question.
So I have this handler for a page that appends some CSS/JS files. The problem is that subsequent requests will result to values appended over and over again.
Example:
def action_index(self):
self.template = 'index.html'
extra_styles = ['/media/css/jquery.lightbox-0.5.css']
extra_scripts = ['/media/js/jquery.lightbox-0.5.min.js', '/media/js/project.js']
for style in extra_styles:
self.styles.append(style)
for script in extra_scripts:
self.scripts.append(script)
How do you usually handle this in platform like Google AppEngine since I'm coming from PHP background where objects only lives within the current request.
Thanks
As requested, here is the base class:
Base controller
from google.appengine.ext.webapp import template
import os
import config
import datetime
class BaseController(object):
request = None
response = None
action = 'index'
method = 'get'
params = []
template_values = {}
template_dir = None
template = None
default_styles = ['/media/bootstrap/css/bootstrap.min.css', '/media/css/style.css']
default_scripts = ['/media/js/jquery-1.6.4.min.js']
styles = []
scripts = []
def __init__(self, request, response, *args, **kwargs):
self.request = request
self.response = response
self.action = 'index'
if 'action' in kwargs and kwargs['action']:
self.action = kwargs['action']
self.method = 'get'
if 'method' in kwargs and kwargs['method']:
self.method = kwargs['method']
self.params = []
if 'params' in kwargs and kwargs['params']:
if isinstance(kwargs['params'], list):
self.params = kwargs['params']
# Initialize template related variables
self.template_values = {}
self.styles = list(self.default_styles)
self.scripts = list(self.default_scripts)
def pre_dispatch(self):
pass
def post_dispatch(self):
if self.template is not None and self.template_dir is not None:
# Populate current year
dt = datetime.date.today()
self.template_values['current_year'] = dt.year
# Populate styles and scripts
self.template_values['styles'] = self.styles
self.template_values['scripts'] = self.scripts
path = os.path.join(config.template_dir, self.template_dir, self.template)
self.response.out.write(template.render(path, self.template_values))
def run_action(self):
action_name = 'action_' + self.action
if hasattr(self, action_name):
action = getattr(self, action_name)
action()
else:
raise Http404Exception('Controller action not found')
def dispatch(self):
self.pre_dispatch()
self.run_action()
self.post_dispatch()
class HttpException(Exception):
"""Http Exception"""
pass
class Http404Exception(HttpException):
"""Http 404 exception"""
pass
class Http500Exception(HttpException):
"""Http 500 exception"""
pass
And the child class
import datetime
from dclab.lysender.controller import BaseController
class ProjectsController(BaseController):
template_dir = 'projects'
def action_index(self):
self.template = 'index.html'
self.styles.extend(['/media/css/jquery.lightbox-0.5.css'])
self.scripts.extend([
'/media/js/jquery.lightbox-0.5.min.js',
'/media/js/project.js'
])
My fault was that I'm assigning a list to another list via reference, not cloning the list. I'm not so aware of this behavior that's why it made me scratching my head the whole night.
You're declaring a whole lot of variables directly under the class definition. Doing this does not define instance members, as you seem to be expecting, but rather defines class variables - the equivalent of 'static' in a language like Java.
Instead of declaring things as class variables, initialize your values inside the constructor - the __init__ method of your class.
I would also strongly recommend using an existing web framework like webapp2 rather than inventing your own.

Resources