I am developing a really simple webapp that searches for a company's stocks.
Here is the JS code (uses AJAX to fetch the company's stock from the server):
document.getElementById("requestQuoteBtn").addEventListener("click", function createQuoteRequest(){
var quoteSymbol = document.getElementById("requestedSymbol").value;
var quoteRequest = createAJAX();
quoteRequest.open('GET', '/quote?sym='+quoteSymbol);
quoteRequest.send();
quoteRequest.onload = function getQuoteRequest(){
if(quoteRequest.status == 200){ // SUCCESSFUL
displayQuoteData(false, JSON.parse(quoteRequest.response)); // basically shows a hidden div with the data
}
else // NO COMPANY W/ THIS SYMBOL FOUND
displayQuoteData(true, null);
};
});
Here is the Flask code:
#app.route("/quote", methods=["GET"])
#login_required
def quote():
requestedSymbol = request.args.get("sym")
if not requestedSymbol:
return "no symbol"
quoteData = lookup(requestedSymbol) # USES AN API TO FETCH COMPANY'S STOCK
if quoteData is None:
return "NONE", 404
else:
return quoteData
The issue is that if the user accesses, for example, this URL:
www.mywebsite.com/quote?sym=AAPL
It will literally show a raw HTML with JSON's data, instead of my website with the data:
{"name":"Apple, Inc.","price":"$245.18","symbol":"AAPL"}
How can I prevent this?
If you simply want to make sure that users do not accidentally access your api endpoint when trying to access your website (aka this is about user experience and your not concerned with adding auth to your API endpoint)
The easiest way is to create separate routes for your api and your client routing
Update:
#app.route("/api/quote", methods=["GET"])
Likewise update:
quoteRequest.open('GET', '/api/quote?sym='+quoteSymbol);
Your client routing will still be:
#app.route("/quote", methods=["GET"])
If you want to make sure that nobody can access your api endpoint then you need to add some sort of authorization to your endpoint.
If you do not secure your API with some authorization then anyone can access the data you return from your server API simply by visiting the route.
Either way setting up separate routes for your API endpoints and client side routes should solve the problem of showing API data instead of your client template when visiting:
mywebsite.com/quote?sym=AAPL
Related
The concepts of webhooks are quite simple, yet I am here for some guidance on how a realtime change in POST request is handled.
Consider a webhook program in FLASK:
#app.route('/webhook', methods = ['POST'])
def webhook():
data = json.loads(request.data)
return data
Such a program will easy return a json POST request. And result will look like this:
{
"passphrase": "SkZSV1YwZz0=",
"timenow": "2021-09-15T21:18:00Z",
"item": "apple",
"quantity": "50",
"account": "9999"
}
Now I have successfully read in the webhook data. Next step is to do something with the data.
Say I would like to search my database 'df' for item: apple and return the price.
def get_item_price(df, item_name):
# Returns price by filtering df.
df = df[df['name'] == item_name]
df.reset_index(inplace = True)
price = df['price']
return price
So, price_of_item = get_item_price(df, item_name) can now be used. But since webhook is a POST request I am using cache to access values from other routes.
cache.set("price_data", price_of_item )
I can get the price of the apple by calling cache from another route. Say:
#app.route("/index")
def index():
price_data_recieved= cache.get("price_data")
# do some stuff based on price_data_recieved, and call a api
return index_template.format(message = price_of_item)
index_template = """
<div> the price of apple is: {price_data_recieved}{some_other_stuff} </div>
"""
However this is not real time, I need to reload the browser every time. Can I make changes to this such that as a new POST request changes my index route output as well. I assume its like a ping to /index and initiate a task.
Resources explored unsuccessfully:
Make a POST request while redirecting in flask
Display data streamed from a Flask view as it updates
Update and render a value from Flask periodically
Execute a function after Flask returns response
I am going with While Loop to check if cache values have changed from the previous stored once for now, until a better method is available.
Let's say I am developing a django REST api that is secured via token and basic authentication. The idea is to list juices. So I am having an URL that lists all juices from specific brands.
So I got a dynamic URL like: path("juices/<str:name_brand>/" ...)
What I want to do now is to show all juices of the brand coca-cola in my frontend where no authentication is required. So all visitors should see the list of juices from coca-cola.
I need to use JS fetch since it has to be asynch. Thus my code is something like:
async function get_juices() {
let juices = await fetch(
`${hostname}/api/v1/juices/coca-cola/
)
result = await juices.json();
return juices;
}
My backend is a simple ListView that dynamically filters by brand.
But my problem now is that the API is private and I can't use my credentials in my frontend for obvious security reasons. Because any user could just read the frontend code and use my credentials to access the juices of other brands too.
I was reading now in some posts that it is possible to have a proxy server which only accepts requests from a specific client and reroutes the request with credentials that are being saved on the proxy server. But I have no idea how to implement this with my django app.
I was wondering if there might be a simpler, quicker solution to access some dynamic URLs in public mode and others that are private?
Or any hints how I could implement a reverse proxy to achieve what I need?
Thanks very much in advance
EDIT:
My view:
class JuicesList(APIView):
def get(self, request, **url_params):
name_brand = url_params.get("name_brand", None)
result = Juices.objects.filter(name_brand=name_brand)
return Response(result)
The default permission classes are "SessionAuthentication" and "TokenAuthentication"
You can use a simple if-else in your view and return an appropriate response with respect to the user and the brand name:
from rest_framework import status
class JuicesList(APIView):
def get(self, request, **url_params):
name_brand = url_params.get("name_brand", None)
if not request.user.is_staff and name_brand != 'coca-cola':
return Response({'Error': 'Permission denied'}, status=status.HTTP_403_FORBIDDEN)
result = Juices.objects.filter(name_brand=name_brand)
return Response(result) # This not look correct? You haven't serialized the queryset?
Moving further, this type of permissions are a bit manual and not very DRY (do not repeat yourself), hence you can use permissions DRF docs:
from rest_framework import permissions
class JuicesPermission(permissions.BasePermission):
def has_permission(self, request, view):
return request.user.is_staff or view.kwargs.get("name_brand", None) == 'coca-cola'
class JuicesList(APIView):
permission_classes = [JuicesPermission]
def get(self, request, **url_params):
name_brand = url_params.get("name_brand", None)
result = Juices.objects.filter(name_brand=name_brand)
return Response(result)
So in my angular js app in service called 'authService' I have the following resources:
var userAreaLogin = $resource('/api/user_area/login');
var userAreaSignup = $resource('/api/user_area/signup');
var session = $resource('/api/user_area/getSession');
var userAreaLogout = $resource('/api/user_area/logout');
but this doesn't feel quite right, I'm using only the get methods, for example:
this.login = function(credentials) {
var user = userAreaLogin.get(credentials, function() {
...
});
};
this.signup = function(userInfo) {
var signup = userAreaSignup.get(userInfo, function() {
...
});
};
I'm confused about what resources to use, should I have something like this?
var session = $resource('/api/user/session');
var userArea = $resource('/api/user');
userArea.get(credentials); //should login the user?
userArea.post(credentials); //should signup the user?
session.delete(); //should logout the user?
session.get(); //should get the sessions of the logged user if any?
By REST sessions are maintained by the client and not by the service. You should use HTTPS and send the username and password with every request (for example with HTTP basic auth headers) instead of using session cookies... (stateless constraint)
Ofc. on the client side you can have login and logout which will change the content of the auth headers sent via AJAX.
You are going to the right direction.
In a well designed REST API you should have something like this.
POST /users/sign_in # Create a user session (signs in)
DELETE /users/sign_out # Delete a user session (signs out)
POST /users # Create a new user resource
GET /users/:id # Get the user resource
Based on these API you can then define your services. I also suggest to use $http which is cleaner, although you'll write few lines of code more.
# Session related methods
Session.create
Session.delete
# User related methods
User.create
User.get
Hope this makes things clearer.
I have been working with Google-bigquery and JavaScript for a little time now, after getting some help here, something i realised is that the you require your Google login details associated with the project to authorise and achieve what your trying to do.
What i am trying to achieve:-
Allow users to visit my page, and view the data. For example i may show some public data based on weather forecast, so i do not require any users authentication,
Currently for research & development purposed i am using I am using OAuth 2.0 for Web Server Applications, i want to get rid of this as we don't need any user data, apart from having my project client-id email-id etc...
I have read on OAuth 2.0 for Server to Server Applications, and there don't seem to be any support for JavaScript so the end-user doesn't have to be involved.
Is there any solution to this or a safe quick fix, i have tried changing the config code from this sample to see what happens but no luck -
var config = {
'client_id' : 'xxxxxxxxxxx.apps.googleusercontent.com',
"iss" : "xxxxxxxxxxxxxxxxxxxxxxxxxx#developer.gserviceaccount.com",
"scope" : "https://www.googleapis.com/auth/bigquery",
"aud" : "https://accounts.google.com/o/oauth2/token",
"exp" : 1328554385,
"iat" : 1328550785
};
What am i missing in here.
Thanks in advance for any help and advice, i have been struggling for a very loong time with this.
Because there is no way to hide a client secret in client-side JavaScript code, there is no way to authorize a client-side JavaScript application to use BigQuery via a server-to-server OAuth flow.
The only solution in this case is to use a server side proxy for your API calls from the JavaScript application. Here's a snippet below of how you could proxy query calls via AppEngine (note: the code below is open to any user, it does do any check to make sure the calls are being run through your particular JavaScript client).
import httplib2
from apiclient.discovery import build
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from oauth2client.appengine import AppAssertionCredentials
# BigQuery API Settings
SCOPE = 'https://www.googleapis.com/auth/bigquery'
PROJECT_ID = 'XXXXXXXXXX' # REPLACE WITH YOUR Project ID
# Create a new API service for interacting with BigQuery
credentials = AppAssertionCredentials(scope=SCOPE)
http = credentials.authorize(httplib2.Http())
bigquery_service = build('bigquery', 'v2', http=http)
class StartQueryHandler(webapp.RequestHandler):
def post(self):
query_string = self.request.get('query')
jobCollection = bigquery_service.jobs()
jobData = {
'configuration': {
'query': {
'query': query_string,
}
}
}
try:
insertResponse = jobCollection.insert(projectId=PROJECT_ID,
body=jobData).execute()
self.response.headers.add_header('content-type',
'application/json',
charset='utf-8')
self.response.out.write(insertResponse)
except:
self.response.out.write('Error connecting to the BigQuery API')
class CheckQueryHandler(webapp.RequestHandler):
def get(self, job_id):
query_job = bigquery_service.jobs()
try:
queryReply = query_job.getQueryResults(projectId=PROJECT_ID,
jobId=job_id).execute()
self.response.headers.add_header('content-type',
'application/json',
charset='utf-8')
self.response.out.write(queryReply)
except:
self.response.out.write('Error connecting to the BigQuery API')
application = webapp.WSGIApplication(
[('/startquery(.*)', StartQueryHandler),
('/checkquery/(.*)', CheckQueryHandler)],
debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
I'm working on an internal web app and we are using secure query string keys generated server side for some simple security to prevent users from accessing pages they haven't been given access to. The page I am currently working on grabs data via AJAX calls and renders it in a table on the page. Each row has an edit button that will take the user to an edit page with more information, with the id of the row kept in the query string. Since every row id is unique, the key for every edit page will be unique to that row-user combination.
My problem is that I need to be able to get these secure query string keys from the server in some way that allows the JavaScript to redirect the user. I can't move the key generator client side because that opens up the possibility of users generating their own keys for pages they don't have permission to visit. And similarly I can't expose the generator in a web service.
Basically what this boils down to is I am stumped in finding a way to send data from the client to the server in order to generate a secure key and then redirect the user to the new page.
Not exactly sure if I am being 100% clear but I'll edit this as questions come in.
Your question is a little unclear, but PageMethods might work for this:
[WebMethod]
public static string GetSecureID()
{
return "Secure";
}
clientRedirectSecure = function() {
PageMethods.GetSecureID(onSuccess, onFailure);
}
onSuccess = function(result) {
window.location.href = "somepage.aspx?id=" + result;
}
onFailure = function(error) {
alert(error);
}
Here's an article that discusses PageMethods:
http://blogs.microsoft.co.il/blogs/gilf/archive/2008/10/04/asp-net-ajax-pagemethods.aspx