OAuth 2.0 for Server to Server Applications using JavaScript - javascript

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()

Related

How can I make a script in python to reload an app in qlik cloud

At this moment I'm trying to reload a Qlik app through a python script, but I had a few problems, I'm gonna explain the things I already tested, but if someone knows how can I solve that, I will appreciate it a lot
The first thing I tried, was the "reloads API" from qlik, to do this you have to send a request with the app id, and with the parameter "Partial" true or false, but even more important, you have to send with this request a JSON web token, so I was searching how to obtain the jwt of qlik and I found this page: "https://qlik.dev/tutorials/create-signed-tokens-for-jwt-authorization", I created all as the page said, and finally I make this code in javascript to test, but this doesn't work:
const fs = require('fs');
const uid = require('uid-safe');
const jwt = require('jsonwebtoken');
const https = require('https')
const payload = {
jti: uid.sync(32), // 32 bytes random string
sub: '(id of my user that appears in assignment users)',
subType: 'user',
name: '(Name of my user)',
email: '(email of my user)',
email_verified: true,
};
const privateKey = fs.readFileSync("path/certificate.pem");
// I don't know the meaning of that 'kid and issuer have to match with the IDP config'
// audience has to be qlik.API/jwt-login-session
const signingOptions = {
keyid: I put = 'my-custom-jwt',
algorithm: I put = 'RS256',
issuer: '(hostname)',
audience: I put = 'qlik.api/login/jwt-session',
};
const myToken = jwt.sign(payload, privateKey, signingOptions);
const qlikUrl = "(hostname)"
const data = JSON.stringify({"appId": "(appId)", "partial": true})
const options = {
hostname: qlikUrl,
port: 443,
path: '/api/v1/reloads',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer '+ myToken
}
}
https.request(options)
req.write(data)
The second thing I did was, try to use the "qsAPI" library of python to connect to my qlik and reload an app specifying their id by a method of this library, but I don't understand what I have to put here, and in the documentation, doesn't say nothing about what's the meaning of the parameters, except ('hostname'). The problem is in the method to do the connection because I have to do this:
qrs = qsAPI.QRS(proxy='hostname', user=('yor_domain', 'username', 'password'))
I say that because if I go to the python IDE, this show which parameters I have to put, and these are different, now I have to put this:
qrs = qsAPI.QRS(proxy='hostname', user=('userDirectory', 'userId', 'password'))
I don't know where I can find the user directory because I don't know
what it is.
The user id (I'm guessing it's the user id that appears in "mapping
users").
The "password", is no problem
But still have an opportunity, because I can connect to Qlik through python if I find where I can download the certificate authentication of a user, but I don't know where is it in qlik cloud.
Can someone help me, please
I havent tried with Python (only with JS/TS) but the approach should be the same.
Couple of things:
imo instead of web token you can try with API Key (for a start). Managing API keys -> Generating an API key from the hub.
the second point is that qsAPI seems to be for Qlik Sense Enterprise on Windows and not for SaaS.
the only Python lib dedicated for Qlik SaaS (that im aware of) is qsaas. Havent used it myself ... just found it on GH
and a bit of a warning regarding the partial: true. Please make sure that you really have to use partial reload. Partial reloads have a specific use case and be careful when using them ... just saying :)
I finally have the solution of that as Stefan Stoichev said before publishing this post, the qsApi seems to be a python library for Qlik Sense Enterprise on Windows, and the correct library for the Qlik Cloud is qsaas, but I'm going to explain every step because I don't want that any person of this world suffer this.
First of all, you have to create an API KEY, in Qlik Cloud, IMPORTANT, you have to save the api_key code that appears in a green text box when you created successfully your API KEY, save this as your dear friend because you will need this code in the future
Subsequently, you have to create a new python code as this:
from qsaas.qsaas import Tenant
import JSON
api_key = <API_KEY>
q = Tenant(api_key=api_key, tenant=<hostname>,
tenant_id=<tenant_id>)
q.post('reloads', json.dumps({'appId': 'dbf3e4ce-c6b3-4190-876c-c443a8691fa6'})))
Don't worry my dear friend if you don't know where is it, the 'hostname' and the 'tenant_id' are in qlik cloud, here is a little tutorial for you:
First login to your qlik
Then click on your profile photo
Click in about, and there you have these two data information

Supabase and Google Apps Script

Is it possible to use Supabase with Google Apps Script (GAS)?
I'm new to Supabase and I've never tried to import external library to GAS before. I'm planning to use Supabase as a database for my project in GAS. The project is built to run a bot in a chatting platform rather than a user interface. Based on the documentation from Supabase, it looks like it works by executing function from the Supabase library.
I have tried to use the Supabase library by copy-pasting the whole library from here or using eval(), but all of them return the error: ReferenceError: self is not defined.
Here is the function I run that uses eval():
function myFunction(){
var x = eval(UrlFetchApp.fetch("https://unpkg.com/#supabase/supabase-js").getContentText())
var supabase = createClient(supabaseUrl,supabaseKey)
}
createClient() is supposed to be a function from the Supabase library.
One possible route that I can think of to make this work is to use Postgrest, which is the underlining API server that Supabase users.
Since every Supabase instance can be accessed using rest API provided by Postgrest, you should be able to perform insert, update, delete and read operations to your Supabase database from your GAS project.
You can probably run something like this to access Supabase.
$anonKey = "YOUR_SUPABASE_ANON_KEY_HERE"
$url = "YOUR_SUPABASE_URL_HERE"
$var options = {
"async": true,
"crossDomain": true,
"method" : "GET",
"headers" : {
"Authorization" : "Bearer " + $anonKey,
"apikey": $anonKey
}
};
var response = UrlFetchApp.fetch($url + "YOUR_QUERY_PARAMETER_FOR_POSTGREST", options);

Fetch data from django backend having parts of dynamic URL private

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)

How to prevent user from accessing JSON data from query parameters?

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

Creating a YouTube Service via ASP.NET using a pre-existing Access Token

I've been working on a Website for users to upload videos to a shared YouTube account for later access. After much work I've been able to get an Active Token, and viable Refresh Token.
However, the code to initialize the YouTubeService object looks like this:
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name,
});
I've already got a token, and I want to use mine. I'm using ASP.NET version 3.5, and so I can't do an async call anyways.
Is there any way I can create a YouTubeService object without the async call, and using my own token? Is there a way I can build a credential object without the Authorization Broker?
Alternatively, the application used YouTube API V2 for quite some time, and had a form that took a token, and did a post action against a YouTube URI that was generated alongside the token in API V2. Is there a way I can implement that with V3? Is there a way to use Javascript to upload videos, and possibly an example that I could use in my code?
NOTE: I ended up upgrading my Framework to 4.5 to access the google libraries.
To programatically initialize a UserCredential Object you've got to build a Flow, and TokenResponse. A Flow Requires a Scope (aka the permissions we are seeking for the credentials.
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Auth.OAuth2.Flows;
string[] scopes = new string[] {
YouTubeService.Scope.Youtube,
YouTubeService.Scope.YoutubeUpload
};
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = XXXXXXXXXX, <- Put your own values here
ClientSecret = XXXXXXXXXX <- Put your own values here
},
Scopes = scopes,
DataStore = new FileDataStore("Store")
});
TokenResponse token = new TokenResponse {
AccessToken = lblActiveToken.Text,
RefreshToken = lblRefreshToken.Text
};
UserCredential credential = new UserCredential(flow, Environment.UserName, token);
Hope that helps.
Currently the official Google .NET client library does not work with .NET Framework 3.5. (Note: this is an old question the library hasn't supported .NET 3.5 since 2014. So the statement would have been valid then as well.) That being said you are not going to be able to create a service for the Google .NET client library using an existing access token. Also not possible to create it with an access token using any .NET Framework you would need to create your own implementation of Idatastore and load a refresh token.
Supported Platforms
.NET Framework 4.5 and 4.6
.NET Core (via netstandard1.3 support)
Windows 8 Apps
Windows Phone 8 and 8.1
Portable Class Libraries
That being said you are going to have to code this yourself from the ground up. I have done it and it is doable.
Authentication :
You have stated you have your refresh token already so I won't go into how to create that.
The following is a HTTP POST call
Refresh access token request:
https://accounts.google.com/o/oauth2/token
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
Refresh Access token response:
{ "access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ", "token_type" : "Bearer", "expires_in" : 3600 }
An call you make to the YouTube API you can either add the access token as the authorization bearer token or you can just take it on to the end of any request
https://www.googleapis.com/youtube/v3/search?access_token={token here}
I have a full post on all of the calls to the auth server Google 3 legged Oauth2 flow. I just use normal webRequets for all my calls.
// Create a request for the URL.
WebRequest request = WebRequest.Create("http://www.contoso.com/default.html");
// If required by the server, set the credentials.
request.Credentials = CredentialCache.DefaultCredentials;
// Get the response.
WebResponse response = request.GetResponse();
// Display the status.
Console.WriteLine (((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server.
Stream dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Display the content.
Console.WriteLine(responseFromServer);
// Clean up the streams and the response.
reader.Close();
response.Close();
Upgrade .NET 4+
If you can upgrade to the newest version of .NET using the library will be much easier. This is from Googles official documentation Web Applications ASP.NET. I have some additional sample code on my github account which shoes how to use the Google Drive API. Google dotnet samples YouTube data v3.
using System;
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Drive.v2;
using Google.Apis.Util.Store;
namespace Google.Apis.Sample.MVC4
{
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "PUT_CLIENT_ID_HERE",
ClientSecret = "PUT_CLIENT_SECRET_HERE"
},
Scopes = new[] { DriveService.Scope.Drive },
DataStore = new FileDataStore("Drive.Api.Auth.Store")
});
public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.
var user = controller.Session["user"];
if (user == null)
{
user = Guid.NewGuid();
controller.Session["user"] = user;
}
return user.ToString();
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
}
Top tip YouTube doesn't support service accounts your going to have to stick with Oauth2. As long as you have authenticated your code once it should continue to work.

Categories