Making credentialed requests with Bokeh AjaxDataSource - ajax

I have a plot set up to use an AjaxDataSource. This is working pretty well in my local development, and was working as deployed in my Kubernetes cluster. However, after I added HTTPS and Google IAP (Identity-Aware Proxy) to my plotting app, all of the requests to the data-url for my AjaxDataSource are rejected by the Google IAP service.
I have run into this issue in the past with other AJAX requests to Google IAP-protected services, and resolved it by setting {withCredentials: true} in my axios requests. However, I do not have this option while working with Bokeh's AjaxDataSource. How do I get BokehJS to pass the cookies to my service in the AjaxDataSource?

AjaxDataSource can pass headers:
ajax_source.headers = { 'x-my-custom-header': 'some value' }
There's not any way to set cookies (that would be set on the viewer's browser... which does not seem relevant in this context). Doing that would require building a custom extension.

Thanks to bigreddot for pointing me in the right direction. I was able to build a custom extension that did what I needed. Here's the source code for that extension:
from bokeh.models import AjaxDataSource
from bokeh.util.compiler import TypeScript
TS_CODE = """
import {AjaxDataSource} from "models/sources";
export class CredentialedAjaxDataSource extends AjaxDataSource {
prepare_request(): XMLHttpRequest {
const xhr = new XMLHttpRequest();
xhr.open(this.method, this.data_url, true);
xhr.withCredentials = true;
xhr.setRequestHeader("Content-Type", this.content_type);
const http_headers = this.http_headers;
for (const name in http_headers) {
const value = http_headers[name];
xhr.setRequestHeader(name, value)
}
return xhr;
}
}
"""
class CredentialedAjaxDataSource(AjaxDataSource):
__implementation__ = TypeScript(TS_CODE)
Bokeh extensions documentation: https://docs.bokeh.org/en/latest/docs/user_guide/extensions.html

Related

How do I send CSRFToken in my axios requests using Nuxt and Django on the backend?

I'm using Django Rest as a backend api, and each API call requires a CSRF Token in the headers. In my "Applications" tab in Developer Tools, I clearly have a "csrftoken" value and I somehow need to extract that with each subsequent POST request that my Nuxt application does (using Nuxt/Axios)
My settings.py looks like this:
CORS_ORIGIN_WHITELIST = (
"http://localhost:3000",
"http://127.0.0.1:3000",
)
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000",
]
CORS_EXPOSE_HEADERS = ['Content-Type', 'X-CSRFToken']
CORS_ALLOW_CREDENTIALS = True
CSRF_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_SAMESITE = "Lax"
CSRF_COOKIE_HTTPONLY = True
SESSION_COOKIE_HTTPONLY = True
I have tried using js-cookies with Cookies.get("csrftoken") which just returns undefined. Is the cookie not accessible because it's set to HTTPONLY`?
What is the recommended step here? Should I create a view in my django backend to generate a CSRF Token, and then before making each request on the frontend, I call this view in my Django app to fetch the token?
E.g
def get_csrf(request):
response = JsonResponse({"detail": "CSRF cookie set"})
response["X-CSRFToken"] = get_token(request)
return response
Not sure how to proceed..
My Nuxt/Axios requests looks something like this:
const response =
await this.$axios.$post("/api/portfolios/", stockData,
{ headers: { "X-CSRFToken": /* Need some value here. */ }
});
I can however get the cookie using nuxtServerInit in my Nuxt Store:
async nuxtServerInit({commit}) {
console.log(this.$cookies.get("csrftoken")) // this works, can store it in some state
},
I can store the value from nuxtServerInit in a Nuxt store. However, whenever I logout, how do I make sure to extract the new csrftoken from the browser? The nuxtServerInit part above only works if I do a page reload, which isn't ideal.
Appreciate any guidance I can get.
Setup axios with default xsrfHeaderName and xsrfCookieName values via nuxt plugin.
When configured, axios will include in request your csrf header with cookie value if it's present in cookies.
in nuxt.config.js include your new plugin
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
'~/plugins/axios',
]
create plugins/axios.js
There is the option to declare as global default config, or for a nuxt instance.
// content of plugins/axios.js
/*
// This is a global config declaration that works on any axios instance,
// meaning that if you just import axios from 'axios' in any place, you will get those.
// This will also work on the axios instance that nuxt creates and injects.
import axios from 'axios'
axios.defaults.xsrfHeaderName = 'x-csrftoken'
axios.defaults.xsrfCookieName = 'csrftoken'
*/
export default function ({ $axios }) {
// This is a nuxt specific instance config, this will work in
// everyplace where nuxt inject axios, like Vue components, and store
$axios.defaults.xsrfHeaderName = 'x-csrftoken'
$axios.defaults.xsrfCookieName = 'csrftoken'
}

Nuxt Apollo with dynamic headers for a session based authentication

Apollo is not storing the header from the query dynamically.
pages/index.vue
methods: {
fetchCars() {
const token = Cookies.get('XSRF-TOKEN')
console.log(token) // 🟢 Token is shown in console
this.$apollo.query({
query: gql`
query {
cars {
uuid
name
}
}
`,
headers: {
'X-XSRF-TOKEN': token, // â­• Fetch without header
},
})
},
},
Is there a way to set the header value new for every Apollo request?
I have a separate Frontend and Backend. For the Frontend I am using Nuxt.js with Apollo. I want to have a session based communication with my server. For this reason I need to send the CSRF-Token with every Request.
Now the problem: On the first load of the page there is no Cookie set on the browser. I do a GET-Request on every initialization of my Nuxt application.
plugins/csrf.js
fetch('http://127.0.0.1:8000/api/csrf-cookie', {
credentials: 'include',
})
Now I have a valid Cookie set on my side and want to communicate with the GraphQL Server but my header is not set dynamically in the query. Does anyone know how I can solve this?
My Laravel Backend is throwing now a 419 Token Mismatch Exception because I did not send a CSRF-Token with my request.
Link to the repository: https://github.com/SuddenlyRust/session-based-auth
[SOLVED] Working solution: https://github.com/SuddenlyRust/session-based-auth/commit/de8fb9c18b00e58655f154f8d0c95a677d9b685b Thanks to the help of kofh in the Nuxt Apollo discord channel 🎉
In order to accomplish this, we need to access the code that gets run every time a fetch happens. This code lives inside your Apollo client's HttpLink. While the #nuxtjs/apollo module gives us many options, we can't quite configure this at such a high level.
Step 1: Creating a client plugin
As noted in the setup section of the Apollo module's docs, we can supply a path to a plugin that will define a clientConfig:
// nuxt.config.js
{
apollo: {
clientConfigs: {
default: '~/plugins/apollo-client.js'
}
}
}
This plugin should export a function which receives the nuxt context. It should return the configuration to be passed to the vue-cli-plugin-apollo's createApolloClient utility. You don't need to worry about that file, but it is how #nuxtjs/apollo creates the client internally.
Step 2: Creating the custom httpLink
In createApolloClient's options, we see we can disable defaultHttpLink and instead supply our own link. link needs to be the output of Apollo's official createHttpLink utility, docs for which can be found here. The option we're most interested in is the fetch option which as the docs state, is
a fetch compatible API for making a request
This boils down to meaning a function that takes uri and options parameters and returns a Promise that represents the network interaction.
Step 3: Creating the custom fetch method
As stated above, we need a function that takes uri and options and returns a promise. This function will be a simple passthrough to the standard fetch method (you may need to add isomorphic-fetch to your dependencies and import it here depending on your setup).
We'll extract your cookie the same as you did in your question, and then set it as a header. The fetch function should look like this:
(uri, options) => {
const token = Cookies.get('XSRF-TOKEN')
options.headers['X-XSRF-TOKEN'] = token
return fetch(uri, options)
}
Putting it all together
Ultimately, your ~/plugins/apollo-client.js file should look something like this:
import { createHttpLink } from 'apollo-link-http'
import fetch from 'isomorphic-fetch'
export default function(context) {
return {
defaultHttpLink: false,
link: createHttpLink({
uri: '/graphql',
credentials: 'include',
fetch: (uri, options) => {
const token = Cookies.get('XSRF-TOKEN')
options.headers['X-XSRF-TOKEN'] = token
return fetch(uri, options)
}
})
}
}

Nuxt Axios Dynamic url

I manage to learn nuxt by using following tutorial
https://scotch.io/tutorials/implementing-authentication-in-nuxtjs-app
In the tutorial, it show that
axios: {
baseURL: 'http://127.0.0.1:3000/api'
},
it is point to localhost, it is not a problem for my development,
but when come to deployment, how do I change the URL based on the browser URL,
if the system use in LAN, it will be 192.168.8.1:3000/api
if the system use at outside, it will be example.com:3000/api
On the other hand, Currently i using adonuxt (adonis + nuxt), both listen on same port (3000).
In future, I might separate it to server(3333) and client(3000)
Therefore the api links will be
localhost:3333/api
192.168.8.1:3333/api
example.com:3333/api
How do I achieve dynamic api url based on browser and switch port?
You don't need baseURL in nuxt.config.js.
Create a plugins/axios.js file first (Look here) and write like this.
export default function({ $axios }) {
if (process.client) {
const protocol = window.location.protocol
const hostname = window.location.hostname
const port = 8000
const url = `${protocol}//${hostname}:${port}`
$axios.defaults.baseURL = url
}
A late contribution, but this question and answers were helpful for getting to this more concise approach. I've tested it for localhost and deploying to a branch url at Netlify. Tested only with Windows Chrome.
In client mode, windows.location.origin contains what we need for the baseURL.
# /plugins/axios-host.js
export default function ({$axios}) {
if (process.client) {
$axios.defaults.baseURL = window.location.origin
}
}
Add the plugin to nuxt.config.js.
# /nuxt.config.js
...
plugins: [
...,
"~/plugins/axios-host.js",
],
...
This question is a year and a half old now, but I wanted to answer the second part for anyone that would find it helpful, which is doing it on the server-side.
I stored a reference to the server URL that I wanted to call as a Cookie so that the server can determine which URL to use as well. I use cookie-universal-nuxt and just do something simple like $cookies.set('api-server', 'some-server') and then pull the cookie value with $cookies.get('api-server') .. map that cookie value to a URL then you can do something like this using an Axios interceptor:
// plguins/axios.js
const axiosPlugin = ({ store, app: { $axios, $cookies } }) => {
$axios.onRequest ((config) => {
const server = $cookies.get('api-server')
if (server && server === 'some-server') {
config.baseURL = 'https://some-server.com'
}
return config
})
}
Of course you could also store the URL in the cookie itself, but it's probably best to have a whitelist of allowed URLs.
Don't forget to enable the plugin as well.
// nuxt.config.js
plugins: [
'~/plugins/axios',
This covers both the client-side and server-side since the cookie is "universal"

Nuxt window is not defined on server-side rendering

I am trying to get the authorization headers from localStorage inside my middleware. Unfortunately this doesn't work on the first page load, because it is server-rendered.
How could I fix this?
const cookieName = 'feathers-jwt';
import { ApolloClient, createNetworkInterface } from 'apollo-client';
import 'isomorphic-fetch';
const API_ENDPOINT = 'http://localhost:3000/graphql';
const networkInterface = createNetworkInterface({ uri: API_ENDPOINT });
networkInterface.use([{
applyMiddleware(req, next) {
if (!req.options.headers) {
req.options.headers = {}; // Create the header object if needed.
}
req.options.headers['authorization'] = window.localStorage.getItem(cookieName);
next();
}
}]);
const apolloClient = new ApolloClient({
networkInterface,
transportBatching: true
});
export default apolloClient;
source: http://dev.apollodata.com/core/network.html
As I understand it, when you're rendering on the server you don't have access to window and document. In apps that render on both the server and in the client, you need to build in a check to see where you are, and handle that accordingly.
You can use this snippet for the detection of where you are:
var canUseDOM = !!(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement
)
Use it to check if you are running server-side or client-side. In your case I would do the following:
If you're server-side you can check the cookies in the HTTP request itself;
If you're client-side you can check your localStorage store instead.
Of course, you can always opt to server-side render your website as an anonymous not authorised user by default. But that would cause the front-end to blink in and out of authorised state and would be annoying for the user.
In your case, I'd try to find authorisation cookies from the actual cookies that are present in your HTTP request.

Not able to make the ajax request with my header given by ember-simple-auth authorizers

I have developed a template (feedheader.hbs) which I am rendering in my feed template like {{ render 'feedheader' feedheader}}.This feedheader contains the username and profile pic of user.I need to send the the requests to http://example.com/api/users/profile/?targetuser=-1 along with my auth header to get the user details.So I made a controller for feedheader and made the ajax requests.I am using ember-simple-auth (fb login ) for authorization.
controller.js
import Ember from 'ember';
import OAuth2Bearer from 'ember-simple-auth/authorizers/oauth2-bearer';
const { service } = Ember.inject;
export default Ember.Controller.extend(OAuth2Bearer,{
session:service('session'),
init:function(){
this.get('session').authorize('authorizer:application', (headerName, headerValue) => {
const headers = {};
headers[headerName] = headerValue;
Ember.$.ajax('http://example.com/api/users/profile/?targetuser=-1', { headers });
});
}
});
But it throws an error in console
"Uncaught Error: Assertion Failed: Expected hash or Mixin instance, got [object Function]"
authorizers(application.js)
import OAuth2Bearer from 'ember-simple-auth/authorizers/oauth2-bearer';
export default OAuth2Bearer.extend();
The authorizers is working fine in my adapter.There is some simple thing I am missing and not able to fix this.Please tell how to send the ajax request with my authorizers header.
The way you're sending the headers doesn't look right to me. Instead of
Ember.$.ajax('http://example.com/api/users/profile/?targetuser=-1', { headers });
it should be
Ember.$.ajax('http://example.com/api/users/profile/?targetuser=-1', { headers: headers });

Resources